<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  <meta name="renderer" content="webkit">
  <meta http-equiv="X-UA-Compatible" content="IE=edge" >
  <link rel="dns-prefetch" href="https://github.com/Baichenghui">
  <title>b9i60000</title>
  <meta name="generator" content="hexo-theme-yilia-plus">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  
    
    <!-- <meta name="keywords" content="">
    <meta name="description" content=""> -->
  
  <meta property="og:type" content="website">
<meta property="og:title" content="b9i60000">
<meta property="og:url" content="https://github.com/Baichenghui/index.html">
<meta property="og:site_name" content="b9i60000">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="b9i60000">
<meta name="twitter:card" content="summary">
  
    <link rel="alternative" href="/atom.xml" title="b9i60000" type="application/atom+xml">
  
  
    <link rel="icon" href="/favicon.ico">
  
  
    <link rel="apple-touch-icon" href="/apple-touch-icon-180x180.png">
  
  <link rel="stylesheet" type="text/css" href="/./main.a5fda8.css">
  <style type="text/css">
    
    #container.show {
      background: linear-gradient(200deg,#a0cfe4,#e8c37e);
    }
  </style>
  

  

  
    
 	  <script src="/lib/clickLove.js"></script>
  
  


  

</head>

<body>
  <div id="container" q-class="show:isCtnShow">
    <canvas id="anm-canvas" class="anm-canvas"></canvas>
    <div class="left-col" q-class="show:isShow">
      


<div class="overlay" style="background: #4d4d4d;"></div>
<div class="intrude-less">
	<header id="header" class="inner">
		<a href="/" class="profilepic">
			<img src="/img/head.jpg" class="js-avatar">
		</a>
		<hgroup>
			<h1 class="header-author"><a href="/">b9i60000</a></h1>
		</hgroup>
		

		<nav class="header-menu">
			<ul>
			
				<li><a href="/" target="_blank">主页</a></li>
			
				<li><a href="https://zhousiwei.gitee.io/ibooks/" target="_blank">技术笔记</a></li>
			
				<li><a href="/tags/%E9%9A%8F%E7%AC%94/" target="_blank">随笔</a></li>
			
			</ul>
		</nav>
		<nav class="header-smart-menu">
			
				
					<a q-on="click: openSlider(e, 'innerArchive')" href="javascript:void(0)">所有文章</a>
				
			
				
					<a q-on="click: openSlider(e, 'friends')" href="javascript:void(0)">友链</a>
				
			
				
					<a q-on="click: openSlider(e, 'aboutme')" href="javascript:void(0)">关于我</a>
				
			
		</nav>
		<nav class="header-nav">
			<div class="social">
				
					
						<a class="github" href="#" 
												title="GitHub" target="_blank"><i class="icon-github"></i></a>
					
				
					
						<a class="github" href="#" 
												title="GitHub" target="_blank"><i class="icon-github"></i></a>
					
				
					
						<a class="gitee" href="#" 
												title="码云" target="_blank"><i class="icon-gitee"></i></a>
					
				
					
						<a class="jianshu" href="#" 
												title="简书" target="_blank"><i class="icon-jianshu"></i></a>
					
				
			</div>
		
		</nav>
	</header>		
</div>

    </div>
    <div class="mid-col" q-class="show:isShow,hide:isShow|isFalse">
      
      
      <a class="forkMe" style="position:absolute;z-index:999;top:0;right:0.5em;" 
        href="https://github.com/JoeyBling/hexo-theme-yilia-plus" target="_blank">
        <img src="/img/forkme.png"
          class="attachment-full size-full" alt="Fork me on GitHub" data-recalc-dims="1"></a>
      
      
<nav id="mobile-nav">
  	<div class="overlay js-overlay" style="background: #4d4d4d"></div>
	<div class="btnctn js-mobile-btnctn">
  		<div class="slider-trigger list" q-on="click: openSlider(e)"><i class="icon icon-sort"></i></div>
	</div>
	<div class="intrude-less">
		<header id="header" class="inner">
			<div class="profilepic">
				<a href="/">
					<img src="/img/head.jpg" class="js-avatar">
				</a>
			</div>
			<hgroup>
			  <h1 class="header-author js-header-author">b9i60000</h1>
			</hgroup>
			
			
			
				
			
				
			
				
			
			
			
			<nav class="header-nav">
				<div class="social">
					
						
						<a class="github" target="_blank" 
							href="#" title="GitHub"><i class="icon-github"></i></a>
						
					
						
						<a class="github" target="_blank" 
							href="#" title="GitHub"><i class="icon-github"></i></a>
						
					
						
						<a class="gitee" target="_blank" 
							href="#" title="码云"><i class="icon-gitee"></i></a>
						
					
						
						<a class="jianshu" target="_blank" 
							href="#" title="简书"><i class="icon-jianshu"></i></a>
						
					
				</div>
			</nav>

			<nav class="header-menu js-header-menu">
				<ul style="width: 70%">
				
				
					<li style="width: 33.333333333333336%"><a href="/">主页</a></li>
		        
					<li style="width: 33.333333333333336%"><a target="_blank" rel="noopener" href="https://zhousiwei.gitee.io/ibooks/">技术笔记</a></li>
		        
					<li style="width: 33.333333333333336%"><a href="/tags/%E9%9A%8F%E7%AC%94/">随笔</a></li>
		        
				</ul>
			</nav>
		</header>				
	</div>
	<div class="mobile-mask" style="display:none" q-show="isShow"></div>
</nav>

      <div id="wrapper" class="body-wrap">
        <div class="menu-l">
          <div class="canvas-wrap">
            <canvas data-colors="#eaeaea" data-sectionHeight="100" data-contentId="js-content" id="myCanvas1"
              class="anm-canvas"></canvas>
          </div>
          <div id="js-content" class="content-ll">
            

  
    <article id="post-mac-终端查看当前使用的什么脚本，以及切换" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
  
  
    <h1 itemprop="name">
	
	  <a class="article-title" href="/2019/10/20/mac-%E7%BB%88%E7%AB%AF%E6%9F%A5%E7%9C%8B%E5%BD%93%E5%89%8D%E4%BD%BF%E7%94%A8%E7%9A%84%E4%BB%80%E4%B9%88%E8%84%9A%E6%9C%AC%EF%BC%8C%E4%BB%A5%E5%8F%8A%E5%88%87%E6%8D%A2/" target="_blank">mac 终端查看当前使用的什么脚本，以及切换</a>
  
    </h1>
  


  
  
<a href="/2019/10/20/mac-%E7%BB%88%E7%AB%AF%E6%9F%A5%E7%9C%8B%E5%BD%93%E5%89%8D%E4%BD%BF%E7%94%A8%E7%9A%84%E4%BB%80%E4%B9%88%E8%84%9A%E6%9C%AC%EF%BC%8C%E4%BB%A5%E5%8F%8A%E5%88%87%E6%8D%A2/" class="archive-article-date">
        <time datetime="2019-10-20T02:58:59.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2019-10-20</time>
</a>

  
  
    

  
  </header>
  
  <div class="article-entry" itemprop="articleBody">
    
    <p>查看<br>echo $SHELL</p>
<p>切换 bash<br>要在macOS上更改用户帐户的默认外壳，只需chsh -s在“终端”窗口中运行（更改外壳）命令。<br>通过运行以下命令将默认Shell更改为Bash：chsh -s / bin / bash</p>
<p>切换 zsh<br>通过运行以下命令，将默认shell更改回Zsh：<br>chsh -s / bin / zsh<br>出现提示时输入密码。关闭终端窗口并重新打开它之后，您将使用Zsh。</p>

    

    

    

  </div>
  <div class="article-info article-info-index">
    
    
    

    
    <p class="article-more-link">
      <a class="article-more-a" href="/2019/10/20/mac-%E7%BB%88%E7%AB%AF%E6%9F%A5%E7%9C%8B%E5%BD%93%E5%89%8D%E4%BD%BF%E7%94%A8%E7%9A%84%E4%BB%80%E4%B9%88%E8%84%9A%E6%9C%AC%EF%BC%8C%E4%BB%A5%E5%8F%8A%E5%88%87%E6%8D%A2/">展开全文 >></a>
    </p>
    

    
    <div class="clearfix"></div>
  </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-macbook-电池健康程度检测终端命令" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
  
  
    <h1 itemprop="name">
	
	  <a class="article-title" href="/2019/10/20/macbook-%E7%94%B5%E6%B1%A0%E5%81%A5%E5%BA%B7%E7%A8%8B%E5%BA%A6%E6%A3%80%E6%B5%8B%E7%BB%88%E7%AB%AF%E5%91%BD%E4%BB%A4/" target="_blank">macbook 电池健康程度检测终端命令</a>
  
    </h1>
  


  
  
<a href="/2019/10/20/macbook-%E7%94%B5%E6%B1%A0%E5%81%A5%E5%BA%B7%E7%A8%8B%E5%BA%A6%E6%A3%80%E6%B5%8B%E7%BB%88%E7%AB%AF%E5%91%BD%E4%BB%A4/" class="archive-article-date">
        <time datetime="2019-10-20T02:58:32.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2019-10-20</time>
</a>

  
  
    

  
  </header>
  
  <div class="article-entry" itemprop="articleBody">
    
    <p>ioreg -rn AppleSmartBattery | grep -i capacity</p>

    

    

    

  </div>
  <div class="article-info article-info-index">
    
    
    

    
    <p class="article-more-link">
      <a class="article-more-a" href="/2019/10/20/macbook-%E7%94%B5%E6%B1%A0%E5%81%A5%E5%BA%B7%E7%A8%8B%E5%BA%A6%E6%A3%80%E6%B5%8B%E7%BB%88%E7%AB%AF%E5%91%BD%E4%BB%A4/">展开全文 >></a>
    </p>
    

    
    <div class="clearfix"></div>
  </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-Gitlab-fastlane-持续集成（小白从0到1）" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
  
  
    <h1 itemprop="name">
	
	  <a class="article-title" href="/2019/10/20/Gitlab-fastlane-%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90%EF%BC%88%E5%B0%8F%E7%99%BD%E4%BB%8E0%E5%88%B01%EF%BC%89/" target="_blank">Gitlab+fastlane 持续集成（小白从0到1）</a>
  
    </h1>
  


  
  
<a href="/2019/10/20/Gitlab-fastlane-%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90%EF%BC%88%E5%B0%8F%E7%99%BD%E4%BB%8E0%E5%88%B01%EF%BC%89/" class="archive-article-date">
        <time datetime="2019-10-20T02:56:58.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2019-10-20</time>
</a>

  
  
    

  
  </header>
  
  <div class="article-entry" itemprop="articleBody">
    
    <p>[TOC]</p>
<h5 id="实现步骤"><a href="#实现步骤" class="headerlink" title="实现步骤"></a>实现步骤</h5><ol>
<li>在gitlab新建项目</li>
<li>配置并启动gitlab-runner</li>
<li>安装并配置fastlane</li>
<li>配置.gitlab-ci.yml文件</li>
</ol>
<h5 id="1、在gitlab新建项目"><a href="#1、在gitlab新建项目" class="headerlink" title="1、在gitlab新建项目"></a>1、在gitlab新建项目</h5><p>做开发的都会</p>
<h5 id="2、配置gitlab-runner"><a href="#2、配置gitlab-runner" class="headerlink" title="2、配置gitlab-runner"></a>2、配置gitlab-runner</h5><p><a target="_blank" rel="noopener" href="https://docs.gitlab.com/runner/register/index.html">gitlab-runner文档</a></p>
<p><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/c7995ad64f48">gitlab-runner的配置——for Mac</a></p>
<h5 id="3、安装并配置fastlane"><a href="#3、安装并配置fastlane" class="headerlink" title="3、安装并配置fastlane"></a>3、安装并配置fastlane</h5><h6 id="安装fastlane"><a href="#安装fastlane" class="headerlink" title="安装fastlane"></a>安装fastlane</h6><p><a target="_blank" rel="noopener" href="https://s0docs0fastlane0tools.icopy.site/">fastlane中文文档</a></p>
<p>注意：任何的安装可能都需要你配置下环境变量</p>
<h6 id="安装firim插件"><a href="#安装firim插件" class="headerlink" title="安装firim插件"></a>安装firim插件</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fastlane add_plugin firim</span><br></pre></td></tr></table></figure>
<p>注意：这个要在工程fastlane文件夹所在目录执行</p>
<h6 id="Appfile"><a href="#Appfile" class="headerlink" title="Appfile"></a>Appfile</h6><p>用于存放 app_identifier（bundle id）、 apple_id(appl开发中账号邮箱地址)、team_id、itc_team_id</p>
<h6 id="Deliverfile"><a href="#Deliverfile" class="headerlink" title="Deliverfile"></a>Deliverfile</h6><p>deliver工具的配置文件</p>
<h6 id="Fastfile"><a href="#Fastfile" class="headerlink" title="Fastfile"></a>Fastfile</h6><p>用于管理你所创建的 lane，lane 则会调用 action。</p>
<h6 id="Gymfile"><a href="#Gymfile" class="headerlink" title="Gymfile"></a>Gymfile</h6><p><a target="_blank" rel="noopener" href="http://docs.fastlane.tools/actions/gym/">官方文档</a></p>
<p><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/f62c8a980c35">Fastlane - gym</a></p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p3931" alt="18aa7066fa1a32b15eca900e3670743f.png"></p>
<h6 id="Snapfile"><a href="#Snapfile" class="headerlink" title="Snapfile"></a>Snapfile</h6><p>用于指定需要进行屏幕截图的设备类型和语种。</p>
<h6 id="Matchfile"><a href="#Matchfile" class="headerlink" title="Matchfile"></a>Matchfile</h6><p><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/5962940522a2">使用Match来管理代码签名</a></p>
<h6 id="Pluginfile"><a href="#Pluginfile" class="headerlink" title="Pluginfile"></a>Pluginfile</h6><p>安装的插件会在这里面显示，如前面提到的插件firim安装之后,Pluginfile中：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gem &#39;fastlane-plugin-firim&#39;</span><br></pre></td></tr></table></figure>

<h5 id="4、配置-gitlab-ci-yml文件"><a href="#4、配置-gitlab-ci-yml文件" class="headerlink" title="4、配置.gitlab-ci.yml文件"></a>4、配置.gitlab-ci.yml文件</h5><p>注意：<br>variables:<br>  LC_ALL: “en_US.UTF-8”<br>  LANG: “en_US.UTF-8”<br>  GIT_STRATEGY: fetch #开发阶段这要设置一下，默认每次都会clone代码，耗时较长。上线版本设置为clone获取最新代码</p>
<p><a target="_blank" rel="noopener" href="https://segmentfault.com/a/1190000019540360">持续集成之.gitlab-ci.yml篇（详细讲解了各命令的作用）</a></p>
<p><a target="_blank" rel="noopener" href="https://s0docs0gitlab0com.icopy.site/ce/ci/yaml/README.html">（官方文档）</a> </p>
<h5 id="原理理解"><a href="#原理理解" class="headerlink" title="原理理解"></a>原理理解</h5><p>在配置.gitlab-ci.yml文件中配置需要执行的脚本，这里配置执行fastlane脚本。</p>
<h5 id="结尾"><a href="#结尾" class="headerlink" title="结尾"></a>结尾</h5><p>个人建议，直接看官方文档，除非英文太差，那就找中文资料吧。</p>

    

    

    

  </div>
  <div class="article-info article-info-index">
    
    
    

    
    <p class="article-more-link">
      <a class="article-more-a" href="/2019/10/20/Gitlab-fastlane-%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90%EF%BC%88%E5%B0%8F%E7%99%BD%E4%BB%8E0%E5%88%B01%EF%BC%89/">展开全文 >></a>
    </p>
    

    
    <div class="clearfix"></div>
  </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-sizeToFit-和-sizeThatFits" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
  
  
    <h1 itemprop="name">
	
	  <a class="article-title" href="/2019/10/20/sizeToFit-%E5%92%8C-sizeThatFits/" target="_blank">sizeToFit 和 sizeThatFits</a>
  
    </h1>
  


  
  
<a href="/2019/10/20/sizeToFit-%E5%92%8C-sizeThatFits/" class="archive-article-date">
        <time datetime="2019-10-20T02:55:22.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2019-10-20</time>
</a>

  
  
    

  
  </header>
  
  <div class="article-entry" itemprop="articleBody">
    
    <p>[toc]</p>
<h5 id="sizeToFit"><a href="#sizeToFit" class="headerlink" title="sizeToFit"></a>sizeToFit</h5><p>sizeToFit: 会计算出最优的 size 而且会改变自己的size</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">UILabel *label &#x3D; [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 0, 0)];</span><br><span class="line">label.backgroundColor &#x3D; UIColor.grayColor;</span><br><span class="line">label.font &#x3D; [UIFont systemFontOfSize:20];</span><br><span class="line">label.text &#x3D; @&quot;北京欢迎你!!!&quot;;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;sizeToFit: 直接改变了label的宽和高, 使它根据上面的字符串的大小做合适的改变</span><br><span class="line">[label sizeToFit];</span><br><span class="line"></span><br><span class="line">NSLog(@&quot;width &#x3D; %.0f height &#x3D; %.0f&quot;,label.frame.size.width, label.frame.size.height);</span><br><span class="line">[self.view addSubview:label];</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;输出结果:</span><br><span class="line">width &#x3D; 119 height &#x3D; 24</span><br><span class="line"></span><br><span class="line">注意: 使用sizeToFit时, 一定要设置控件的frame, 并且不可以使用Masonry, 否则sizeToFit将失效</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h5 id="sizeThatFits"><a href="#sizeThatFits" class="headerlink" title="sizeThatFits"></a>sizeThatFits</h5><p>sizeThatFits: 会计算出最优的 size 但是不会改变 自己的size</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">UILabel *label &#x3D; [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 0, 0)];</span><br><span class="line">label.backgroundColor &#x3D; UIColor.grayColor;</span><br><span class="line">label.font &#x3D; [UIFont systemFontOfSize:20];</span><br><span class="line">label.text &#x3D; @&quot;北京欢迎你!!!&quot;;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;sizeThatFits并没有改变原始label的大小</span><br><span class="line">CGSize sizeThatFits &#x3D;  [label sizeThatFits:CGSizeZero];</span><br><span class="line">NSLog(@&quot;sizeThatFits: width &#x3D; %.1f height &#x3D; %.1f&quot;, sizeThatFits.width, sizeThatFits.height);</span><br><span class="line"></span><br><span class="line">NSLog(@&quot;label: width &#x3D; %.1f height &#x3D; %.1f&quot;,label.frame.size.width, label.frame.size.height);</span><br><span class="line"></span><br><span class="line">[self.view addSubview:label];</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">输出结果:</span><br><span class="line">sizeThatFits: width &#x3D; 119.0 height &#x3D; 24.0</span><br><span class="line">label: width &#x3D; 0.0 height &#x3D; 0.0</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h5 id="iOS-sizeToFit-和-sizeThatFits的联系"><a href="#iOS-sizeToFit-和-sizeThatFits的联系" class="headerlink" title="iOS sizeToFit 和 sizeThatFits的联系"></a>iOS sizeToFit 和 sizeThatFits的联系</h5><p>如果你的控件对尺寸有严格的限定，比如有一个统一的宽高比或者是固定尺寸，那么最好能实现系统给出的约定成俗的接口。<br>sizeToFit 用在基于 frame 布局的情况下，由你的控件去实现 sizeThatFits: 方法：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">- (CGSize)sizeThatFits:(CGSize)size &#123;</span><br><span class="line">    CGSize fitSize &#x3D; [super sizeThatFits:size];</span><br><span class="line">    fitSize.height +&#x3D; self.label.frame.size.height;</span><br><span class="line">    &#x2F;&#x2F; 如果是固定尺寸，就像 UISwtich 那样返回一个固定 Size 就 OK 了</span><br><span class="line">    return fitSize;</span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure>

<p>然后在外部调用该控件的 sizeToFit 方法，这个方法内部会自动调用 sizeThatFits 并更新自身的 Size：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[self.customView sizeToFit]; </span><br></pre></td></tr></table></figure>

    

    

    

  </div>
  <div class="article-info article-info-index">
    
    
    

    
    <p class="article-more-link">
      <a class="article-more-a" href="/2019/10/20/sizeToFit-%E5%92%8C-sizeThatFits/">展开全文 >></a>
    </p>
    

    
    <div class="clearfix"></div>
  </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-刨根问底之-translatesAutoresizingMaskIntoConstraints" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
  
  
    <h1 itemprop="name">
	
	  <a class="article-title" href="/2019/10/20/%E5%88%A8%E6%A0%B9%E9%97%AE%E5%BA%95%E4%B9%8B-translatesAutoresizingMaskIntoConstraints/" target="_blank">刨根问底之 translatesAutoresizingMaskIntoConstraints</a>
  
    </h1>
  


  
  
<a href="/2019/10/20/%E5%88%A8%E6%A0%B9%E9%97%AE%E5%BA%95%E4%B9%8B-translatesAutoresizingMaskIntoConstraints/" class="archive-article-date">
        <time datetime="2019-10-20T02:55:08.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2019-10-20</time>
</a>

  
  
    

  
  </header>
  
  <div class="article-entry" itemprop="articleBody">
    
    <p>我们查看官方文档，可以看到文档是这样介绍该属性的：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">它是一个用来决定，是否将视图的自动调整大小的遮罩(autoresizing mask)转换为 Auto Layout 约束的布尔值。</span><br></pre></td></tr></table></figure>
<p>通过文档介绍我们可以得知：当该属性为 true 时，系统会自动通过视图的 autoresizing mask 创建一组视图的约束，这些约束是基于你提供的 frame、bounds、center 这些属性。也就是说，当你给视图的 frame 赋值之后，它会为你创建静态的、基于 frame 的 Auto Layout 约束。如下：</p>
<h5 id="属性值为-true且指定-frame"><a href="#属性值为-true且指定-frame" class="headerlink" title="属性值为 true且指定 frame"></a>属性值为 true且指定 frame</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">let exampleView &#x3D; UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))</span><br><span class="line">&#x2F;&#x2F; 系统根据你指定的frame 给 exampleView 创建静态的 Auto Layout 约束</span><br><span class="line">exampleView.backgroundColor &#x3D; UIColor.green</span><br><span class="line">view.addSubview(exampleView)</span><br></pre></td></tr></table></figure>

<p>复制代码在上述情况中，你是不能给视图添加额外的约束来修改它的位置或大小的，如果添加额外的约束会导致约束冲突。如下：</p>
<h5 id="属性值为-true，指定-frame-且添加额外约束"><a href="#属性值为-true，指定-frame-且添加额外约束" class="headerlink" title="属性值为 true，指定 frame 且添加额外约束"></a>属性值为 true，指定 frame 且添加额外约束</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F; 例 1-1面的代码下添加此代码，会导致约束冲突</span><br><span class="line">NSLayoutConstraint.activate([</span><br><span class="line">    exampleView.widthAnchor.constraint(equalToConstant: 50)</span><br><span class="line">    ])</span><br><span class="line">    </span><br></pre></td></tr></table></figure>

<p>复制代码错误提示如下图所示：<br><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p3903" alt="3aca4f9fb265f63d5204dcadd60e448a.png"></p>
<h5 id="添加额外约束导致约束冲突"><a href="#添加额外约束导致约束冲突" class="headerlink" title="添加额外约束导致约束冲突"></a>添加额外约束导致约束冲突</h5><p>如果你想使用 Auto Layout 动态计算、改变视图尺寸的话，你必须将该属性值改为 false 。然后你只需提供无歧义、无冲突的约束即可。如例 1-3 代码所示：<br>例 1-3 属性值为 false</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F; 布局效果等同于 例 1-1</span><br><span class="line">let exampleView &#x3D; UIView(frame: .zero)</span><br><span class="line">exampleView.backgroundColor &#x3D; UIColor.green</span><br><span class="line">view.addSubview(exampleView)</span><br><span class="line">&#x2F;&#x2F; 使用 Auto Layout 时，务必将此属性值设为 false</span><br><span class="line">exampleView.translatesAutoresizingMaskIntoConstraints &#x3D; false</span><br><span class="line">NSLayoutConstraint.activate([</span><br><span class="line">    exampleView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100),</span><br><span class="line">    exampleView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 100),</span><br><span class="line">    exampleView.widthAnchor.constraint(equalToConstant: 100),</span><br><span class="line">    exampleView.heightAnchor.constraint(equalToConstant: 100)</span><br><span class="line">    ])</span><br><span class="line">    </span><br></pre></td></tr></table></figure>

<p>复制代码当你代码创建视图时，视图的 translatesAutoresizingMaskIntoConstraints 默认为 true，当你使用 Interface Builder 时，系统会自动将 translatesAutoresizingMaskIntoConstraints 的值设为 false。</p>
<h5 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h5><p>代码创建视图时，视图的 translatesAutoresizingMaskIntoConstraints 属性值默认为 true</p>
<p>Interface Builder 中创建视图时，系统会自动将视图的 translatesAutoresizingMaskIntoConstraints 属性值设为 false</p>
<p>代码创建的视图，且使用 frame 进行布局时，不能添加额外的约束，会导致约束冲突</p>
<p>代码创建的视图，且使用 Auto Layout 进行布局时，需将属性值设为 false</p>
<h5 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h5><p><a target="_blank" rel="noopener" href="https://developer.apple.com/documentation/uikit/uiview/1622572-translatesautoresizingmaskintoco">translatesAutoresizingMaskIntoConstraints</a><br><a target="_blank" rel="noopener" href="https://stackoverflow.com/questions/47800210/when-should-translatesautoresizingmaskintoconstraints-be-set-to-true">When should translatesAutoresizingMaskIntoConstraints be set to true?</a></p>

    

    

    

  </div>
  <div class="article-info article-info-index">
    
    
    

    
    <p class="article-more-link">
      <a class="article-more-a" href="/2019/10/20/%E5%88%A8%E6%A0%B9%E9%97%AE%E5%BA%95%E4%B9%8B-translatesAutoresizingMaskIntoConstraints/">展开全文 >></a>
    </p>
    

    
    <div class="clearfix"></div>
  </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-UICollectionView间隙问题" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
  
  
    <h1 itemprop="name">
	
	  <a class="article-title" href="/2019/10/20/UICollectionView%E9%97%B4%E9%9A%99%E9%97%AE%E9%A2%98/" target="_blank">UICollectionView间隙问题</a>
  
    </h1>
  


  
  
<a href="/2019/10/20/UICollectionView%E9%97%B4%E9%9A%99%E9%97%AE%E9%A2%98/" class="archive-article-date">
        <time datetime="2019-10-20T02:54:54.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2019-10-20</time>
</a>

  
  
    

  
  </header>
  
  <div class="article-entry" itemprop="articleBody">
    
    <p>func fixSlit(inout rect: CGRect, colCount: CGFloat, space: CGFloat = 0) -&gt; CGFloat {<br>    let totalSpace = (colCount - 1) * space // 总共留出的距离<br>    let itemWidth = (rect.width - totalSpace) / colCount  // 按照真实屏幕算出的cell宽度<br>    let fixValue = 1 / UIScreen.mainScreen().scale （1px=0.5pt,6p为3px=1pt）<br>    var realItemWidth = floor(itemWidth) + fixValue // 取整加fixValue<br>    if realItemWidth &lt; itemWidth { // 有可能原cell宽度小数点后一位大于0.5<br>        realItemWidth += 0.5<br>    }<br>    let realWidth = colCount * realItemWidth + totalSpace // 算出屏幕等分后满足<code>1px=0.5pt</code>实际的宽度<br>    let pointX = (realWidth - rect.width) / 2 // 偏移距离<br>    rect.origin.x = -pointX // 向左偏移<br>    rect.size.width = realWidth<br>    return (rect.width - totalSpace) / colCount // 每个cell真实宽度<br>}</p>

    

    

    

  </div>
  <div class="article-info article-info-index">
    
    
    

    
    <p class="article-more-link">
      <a class="article-more-a" href="/2019/10/20/UICollectionView%E9%97%B4%E9%9A%99%E9%97%AE%E9%A2%98/">展开全文 >></a>
    </p>
    

    
    <div class="clearfix"></div>
  </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-Universal-Links-通用链接" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
  
  
    <h1 itemprop="name">
	
	  <a class="article-title" href="/2019/10/20/Universal-Links-%E9%80%9A%E7%94%A8%E9%93%BE%E6%8E%A5/" target="_blank">Universal Links 通用链接</a>
  
    </h1>
  


  
  
<a href="/2019/10/20/Universal-Links-%E9%80%9A%E7%94%A8%E9%93%BE%E6%8E%A5/" class="archive-article-date">
        <time datetime="2019-10-20T02:53:23.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2019-10-20</time>
</a>

  
  
    

  
  </header>
  
  <div class="article-entry" itemprop="articleBody">
    
    <p>[TOC]</p>
<h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>iOS9 之后，才可以支持通用链接。当您支持通用链接时，iOS用户可以点击指向您网站的链接，然后无缝链接到已安装的应用程序，而无需通过Safari。如果未安装您的应用程序，请点击指向您网站的链接以在Safari中打开您的网站。</p>
<h3 id="通用链接对比自定义URL方案的关键优势。"><a href="#通用链接对比自定义URL方案的关键优势。" class="headerlink" title="通用链接对比自定义URL方案的关键优势。"></a>通用链接对比自定义URL方案的关键优势。</h3><ol>
<li><code>独特</code>:与自定义URL方案不同，其他应用无法声明通用链接，因为它们使用的是标准的HTTP 或 HTTPS 链接到你的网站。</li>
<li><code>安全</code>:当用户安装您的应用程序时，iOS会检查您已上传到Web服务器的文件，以确保您的网站允许您的应用程序代表其打开URL。只要您可以创建和上传此文件，因此网站与应用程序的关联是安全的。</li>
<li><code>灵活</code>:即使未安装您的应用程序，通用链接也可以使用。如果未安装您的应用程序，则按用户期望，点击指向您网站的链接可在Safari中打开内容。</li>
<li><code>简单</code>:一个URL既适用于您的网站，也适用于您的应用程序。</li>
<li><code>私人的</code>:其他应用可以与您的应用通信，而无需知道您的应用是否已安装。</li>
</ol>
<p>注意：</p>
<p>通用链接可让用户在点击WKWebView，UIWebView视图和Safari页面中的指向您网站的链接时打开您的应用程序，此外，这些链接还会导致调用openURL:，例如在Mail，Messages和其他应用程序中发生的链接。</p>
<p>当用户在Safari中浏览您的网站时，他们点击指向当前网页所在域中URL的通用链接，iOS会尊重该用户最有可能的意图并在Safari中打开该链接。如果用户点击指向其他域中URL的通用链接，iOS将在您的应用中打开该链接。</p>
<p>对于运行9.0之前的iOS版本的用户，点击指向您网站的通用链接可在Safari中打开该链接。</p>
<h3 id="怎么使用Universal-Links"><a href="#怎么使用Universal-Links" class="headerlink" title="怎么使用Universal Links"></a>怎么使用Universal Links</h3><ol start="0">
<li><p>先决条件：你必须有一个域名,且这个域名需要支持https。</p>
</li>
<li><p>开启 Associated Domains 域<br><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2356" alt="e9116eb0cfb298c7c4835fbbff6699dd.png"></p>
</li>
</ol>
<p>进入苹果Apple Developer — Member Center - Certificates, Identifiers &amp; Profiles – Identifiers - App IDs –Edit 然后开启打钩 Associated Domains 后保存。</p>
<ol start="2">
<li><p>添加 Associated Domains 域<br> 打开工程配置中的Associated Domains，在其中的Domains中填入你想支持的域名，必须以applinks:为前缀。<img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2357" alt="0f4639afb4a83aead318d1962f43fbda.png"></p>
</li>
<li><p>创建一个apple-app-site-association包含有关您的应用程序可以处理的URL的JSON数据的文件。这个文件名必须为apple-app-site-association，没有后缀名.</p>
</li>
</ol>
<p>apple-app-site-association文件:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    &quot;applinks&quot;: &#123;</span><br><span class="line">        &quot;apps&quot;: [],</span><br><span class="line">        &quot;details&quot;: [</span><br><span class="line">            &#123;</span><br><span class="line">                &quot;appID&quot;: &quot;9JA89QQLNQ.com.apple.wwdc&quot;,</span><br><span class="line">                &quot;paths&quot;: [ &quot;&#x2F;wwdc&#x2F;news&#x2F;&quot;, &quot;&#x2F;videos&#x2F;wwdc&#x2F;2015&#x2F;*&quot;]</span><br><span class="line">            &#125;,</span><br><span class="line">            &#123;</span><br><span class="line">                &quot;appID&quot;: &quot;ABCD1234.com.apple.wwdc&quot;,</span><br><span class="line">                &quot;paths&quot;: [ &quot;*&quot; ]</span><br><span class="line">            &#125;</span><br><span class="line">        ]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>appID：组成方式是 teamId.yourapp’s bundle identifier。如上面的 9JA89QQLNQ就是teamId。登陆开发者中心，在Account - Membership里面可以找到Team ID。</p>
<p>paths：设定你的app支持的路径列表，只有这些指定的路径的链接，才能被app所处理。星号的写法代表了可识别域名下所有链接。</p>
<ol start="4">
<li><p>将apple-app-site-association文件上传到您的HTTPS Web服务器。您可以将文件放置在服务器的根目录或.well-known子目录中. 上传完后,自己先访问一下,看看是否能够获取到，当你在浏览器中输入这个文件链接后，应该是直接下载apple-app-site-association文件。</p>
</li>
<li><p>准备您的应用程序以处理通用链接。</p>
</li>
<li><p>进入app后的处理<br> 现在用户点击某个链接，直接可以进我们的app了，但是我们的目的是要能够获取到用户进来的链接，根据链接来展示给用户相应的内容。<br>我们需要在工程里的 AppDelegate 里实现方法</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler</span><br><span class="line">&#123;</span><br><span class="line">    if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb])</span><br><span class="line">    &#123;</span><br><span class="line">        NSURL *url &#x3D; userActivity.webpageURL;</span><br><span class="line">        if (url是我们希望处理的)</span><br><span class="line">        &#123;</span><br><span class="line">            &#x2F;&#x2F;进行我们的处理</span><br><span class="line">        &#125;</span><br><span class="line">        else</span><br><span class="line">        &#123;</span><br><span class="line">            [[UIApplication sharedApplication] openURL:url];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    return YES;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

</li>
</ol>
<h3 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h3><p>在iOS设备中的备忘录中输入App能识别的链接，然后直接点击此链接，就会直接跳转到你的app了。或是长按，在出现的弹出菜单中第二项是在’XXX’中打开，这也代表着成功：</p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2358" alt="fd06b12cf48d7696c55a54b8e89a8a36.png"></p>
<p>或是你将要测试的网址在safari中打开，在出现的网页上方下滑，可以看到有在”XX”应用中打开：</p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2359" alt="7604062dbe0dbbc5a32bbf9465e9dda0.png"></p>
<p>在微信的网页浏览器中也是可以的，虽然微信屏蔽了所有的scheme方式跳转到其它app，但是Universal Links是由系统直接处理的，微信屏蔽不了，这也就实现了从微信跳转到我们的app。</p>
<p>苹果为了方便开发者，提供了一个网页来验证我们编写的这个apple-app-site-association是否合法有效，进入<a target="_blank" rel="noopener" href="https://link.jianshu.com/?t=https://search.developer.apple.com/appsearch-validation-tool/">验证网址</a>进行验证：</p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2360" alt="2594434ea57c0d0fd24e7182f1f15905.png"></p>
<h3 id="注意"><a href="#注意" class="headerlink" title="注意"></a>注意</h3><p>1.apple-app-site-association文件不能带后缀，务必把”.json”的后缀去掉！有些人的电脑是隐藏文件后缀的，这要格外注意；</p>
<p>2.apple-app-site-association一定要传到域名根目录下；</p>
<p>3.WebServer需要支持https，且https使用有效的证书（是私密链接）。笔者在这里卡了很久，原因就是后台给配的https使用无效的证书，导致应用安装的时候不能正确的下载apple-app-site-association文件；</p>
<p>4.如果想测试这个功能，可以让后台搭一个测试服务器，并配置虚拟主机生成域名，然后iOS这边通过host访问域名就可以了。注意”applink:”后面写的一定是域名，不能是IP；</p>
<p>5.抓包的结果显示，网络顺畅的情况下，应用会在在刚安装（不是打开）的时候会去applink中的地址下载apple-app-site-association文件，所以如果需要测试，请保证网络通畅；</p>
<p>6.当所有都准备好，需要测试该功能的时候，只需要在记事本或短信中输入App能识别的链接，然后直接点击或是长按就可以了，直接点的效果是跳转到你的App，然后右上角是“去网页”的箭头，长按的效果是弹出的菜单中第二项是“在’XXX’中打开”，这也代表着成功。直接在Safari中输入链接是无效的，必须从一处跳入才可以（比如上一级网页）。</p>
<p>7.苹果有个网址（这里）可以检测你的apple-app-site-association是否是有效的，准备好了可以测试一下。</p>
<p>8.测试的时候，建议使用dev证书打包，之后安装到手机上测试功能。未安装应用的情况下直接在手机上跑好像也是可以的，因为抓到过请求。</p>
<p>9.如果你有问题，但上面没有列出，Stackoverflow上面有个问题，可以看下其他人的建议。 </p>
<p>10.前端开发经常面临跨域问题，必须要求跨域，如果不跨域，就不行。<br>只有当前webview的url域名，与跳转目标url域名不一致时，Universal Link 才生效。</p>
<h3 id="学习"><a href="#学习" class="headerlink" title="学习"></a>学习</h3><p><a target="_blank" rel="noopener" href="https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html#//apple_ref/doc/uid/TP40016308-CH12-SW1">Apple官方文档</a></p>
<p><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/d1e0c1886046">iOS开发 - Universal Links 通用链接</a></p>
<p><a target="_blank" rel="noopener" href="http://strivingboy.github.io/blog/2015/09/27/ios9/">博客</a></p>
<p><a href="">WWDC2019内参，小专栏</a></p>

    

    

    

  </div>
  <div class="article-info article-info-index">
    
    
    

    
    <p class="article-more-link">
      <a class="article-more-a" href="/2019/10/20/Universal-Links-%E9%80%9A%E7%94%A8%E9%93%BE%E6%8E%A5/">展开全文 >></a>
    </p>
    

    
    <div class="clearfix"></div>
  </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-iOS-长连接-socket-股票方向研究" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
  
  
    <h1 itemprop="name">
	
	  <a class="article-title" href="/2019/10/20/iOS-%E9%95%BF%E8%BF%9E%E6%8E%A5-socket-%E8%82%A1%E7%A5%A8%E6%96%B9%E5%90%91%E7%A0%94%E7%A9%B6/" target="_blank">iOS 长连接 socket 股票方向研究</a>
  
    </h1>
  


  
  
<a href="/2019/10/20/iOS-%E9%95%BF%E8%BF%9E%E6%8E%A5-socket-%E8%82%A1%E7%A5%A8%E6%96%B9%E5%90%91%E7%A0%94%E7%A9%B6/" class="archive-article-date">
        <time datetime="2019-10-20T02:53:03.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2019-10-20</time>
</a>

  
  
    

  
  </header>
  
  <div class="article-entry" itemprop="articleBody">
    
    <p>[TOC]</p>
<h3 id="网络中进程通信"><a href="#网络中进程通信" class="headerlink" title="网络中进程通信"></a>网络中进程通信</h3><p>在了解socket之前，先了解下网络进程之间如何通信。</p>
<h5 id="本地进程间通信"><a href="#本地进程间通信" class="headerlink" title="本地进程间通信"></a>本地进程间通信</h5><ol>
<li>消息传递（管道、消息队列、FIFO）</li>
<li>同步（互斥量、条件变量、读写锁、文件和写记录锁、信号量）</li>
<li>共享内存（匿名的和具名的，eg:channel）</li>
<li>远程过程调用(RPC)</li>
</ol>
<h5 id="网络中进程如何通信"><a href="#网络中进程如何通信" class="headerlink" title="网络中进程如何通信"></a>网络中进程如何通信</h5><p>我们要理解网络中进程如何通信，得解决两个问题：<br>　　ａ、我们要如何标识一台主机，即怎样确定我们将要通信的进程是在那一台主机上运行。<br>　　ｂ、我们要如何标识唯一进程，本地通过pid标识，网络中应该怎样标识？</p>
<p>解决办法：<br>　　ａ、TCP/IP协议族已经帮我们解决了这个问题，网络层的“ip地址”可以唯一标识网络中的主机<br>　　ｂ、传输层的“协议+端口”可以唯一标识主机中的应用程序（进程），因此，我们利用三元组（ip地址，协议，端口）就可以标识网络的进程了，网络中的进程通信就可以利用这个标志与其它进程进行交互。</p>
<h3 id="socket"><a href="#socket" class="headerlink" title="socket"></a>socket</h3><h5 id="socket是什么"><a href="#socket是什么" class="headerlink" title="socket是什么"></a>socket是什么</h5><p>socket是对TCP/IP协议的封装，它的出现只是使得程序员更方便地使用TCP/IP协议栈而已。socket本身并不是协议，它是应用层与TCP/IP协议族通信的中间软件抽象层，是一组调用接口（TCP/IP网络的API函数）</p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2395" alt="dbe646028538d1b1f3890fa8e88b07b9.jpeg"></p>
<h5 id="解决了什么问题"><a href="#解决了什么问题" class="headerlink" title="解决了什么问题"></a>解决了什么问题</h5><p>Socket主要是用来解决网络通信的，那么我们就来理解网络中进程是如何通信的。</p>
<p>Socket编程进行的是端到端的通信，基于网络层和传输层的实现。在网络层，Socket 函数需要指定到底是 IPv4 还是IPv6。传输层需要指定是tcp还是udp。 </p>
<h5 id="Socket怎么通信"><a href="#Socket怎么通信" class="headerlink" title="Socket怎么通信"></a>Socket怎么通信</h5><p>socket 这个中间件是利用三元组（ip地址，协议，端口）解决网络通信的。</p>
<h6 id="通信过程"><a href="#通信过程" class="headerlink" title="通信过程"></a>通信过程</h6><p>通信过程图解：</p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2396" alt="7c9b474bf0101675c98b957b6296d83a.jpeg"></p>
<p>图解说明：</p>
<p>服务器端先初始化Socket，然后与端口绑定(bind)，对端口进行监听(listen)，调用accept阻塞，等待客户端连接。</p>
<p>这时如果有个客户端初始化一个Socket，然后连接服务器(connect)，如果连接成功，这时客户端与服务器端的连接就建立了。客户端发送数据请求，服务器端接收请求并处理请求，然后把回应数据发送给客户端，客户端读取数据，最后关闭连接，一次交互结束。</p>
<h6 id="socket中TCP的三次握手建立连接详解"><a href="#socket中TCP的三次握手建立连接详解" class="headerlink" title="socket中TCP的三次握手建立连接详解"></a>socket中TCP的三次握手建立连接详解</h6><p>我们知道tcp建立连接要进行“三次握手”，即交换三个分组。大致流程如下：</p>
<p>客户端向服务器发送一个SYN J<br>服务器向客户端响应一个SYN K，并对SYN J进行确认ACK J+1<br>客户端再想服务器发一个确认ACK K+1<br>只有就完了三次握手，但是这个三次握手发生在socket的那几个函数中呢？</p>
<p>三次握手建立连接图解：</p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2401" alt="97a37d22000293ba24e53d981d8f3e3e.png"></p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2399" alt="8aa123878e3754db1357220a12cb393c.png"></p>
<p>三次握手建立连接图解说明：</p>
<p>当客户端调用connect时，触发了连接请求，向服务器发送了SYN J包，这时connect进入阻塞状态；<br>服务器监听到连接请求，即收到SYN J包，调用accept函数接收请求向客户端发送SYN K ，ACK J+1，这时accept进入阻塞状态；<br>客户端收到服务器的SYN K ，ACK J+1之后，这时connect返回，并对SYN K进行确认；<br>服务器收到ACK K+1时，accept返回，至此三次握手完毕，连接建立。</p>
<p>形象比喻：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[Shake 1] 套接字A：“你好，套接字B，我这里有数据要传送给你，建立连接吧。”</span><br><span class="line">[Shake 2] 套接字B：“好的，我这边已准备就绪。”</span><br><span class="line">[Shake 3] 套接字A：“谢谢你受理我的请求。</span><br></pre></td></tr></table></figure>

<p>总结：客户端的connect在三次握手的第二次返回，而服务器端的accept在三次握手的第三次返回。</p>
<h6 id="socket中TCP的四次握手释放连接详解"><a href="#socket中TCP的四次握手释放连接详解" class="headerlink" title="socket中TCP的四次握手释放连接详解"></a>socket中TCP的四次握手释放连接详解</h6><p>socket中的四次握手释放连接的过程</p>
<p>四次握手释放连接图解：</p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2400" alt="214f0d393c12d1ad5061cc76ac1211f2.png"></p>
<p>四次握手释放连接图解说明：</p>
<p>某个应用进程首先调用close主动关闭连接，这时TCP发送一个FIN M；<br>另一端接收到FIN M之后，执行被动关闭，对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程，因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据；<br>一段时间之后，接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N；<br>接收到这个FIN的源发送端TCP对它进行确认。<br>这样每个方向上都有一个FIN和ACK。</p>
<p>形象比喻：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[Shake 1] 套接字A：“任务处理完毕，我希望断开连接。”</span><br><span class="line">[Shake 2] 套接字B：“哦，是吗？请稍等，我准备一下。”</span><br><span class="line">等待片刻后……</span><br><span class="line">[Shake 3] 套接字B：“我准备好了，可以断开连接了。”</span><br><span class="line">[Shake 4] 套接字A：“好的，谢谢合作。”</span><br></pre></td></tr></table></figure>

<h3 id="GCDAsyncSocket"><a href="#GCDAsyncSocket" class="headerlink" title="GCDAsyncSocket"></a>GCDAsyncSocket</h3><h5 id="解决了什么问题-1"><a href="#解决了什么问题-1" class="headerlink" title="解决了什么问题"></a>解决了什么问题</h5><p>CocoaAsyncSocket是谷歌的开发者，基于BSD-Socket写的一个IM框架，它给Mac和iOS提供了易于使用的、强大的异步套接字库，向上封装出简单易用OC接口。省去了我们面向Socket以及数据流Stream等繁琐复杂的编程。 </p>
<h5 id="二次封装"><a href="#二次封装" class="headerlink" title="二次封装"></a>二次封装</h5><h6 id="场景"><a href="#场景" class="headerlink" title="场景"></a>场景</h6><p>股票类：</p>
<p>数据量大：5000多支股票，每支股票有分时，分笔数据，画一条k线，可能要500条分时数据，甚至更多。</p>
<p>实时性：股价每分每秒都在变化，一分钟产生很多条数据，用户要看到最新的信息，真的是一秒钟上下几十万啊。</p>
<p>服务器主动推：股票预警等等一些重要消息，需要服务器实时推送，保证客户端100%收到，收不到可能造成客户资金损失。</p>
<p>股票的数据不能通过HTTP协议来传输了，只能走TCP协议了，当然一些个人信息什么的还是走HTTP的。</p>
<h6 id="技术点"><a href="#技术点" class="headerlink" title="技术点"></a>技术点</h6><p>1、数据量比较大：</p>
<p>一次请求可能返回几百条数据到移动端，对数据要进行压缩，这里采用了 google 的 Protocol Buffer 数据传输格式，因为它对象序列化速度快，压缩率高 (ps: http 一般用 json, xml)。</p>
<p>2、实时性与主动推：</p>
<p>服务器与客户端之间维护一条 tcp 长连接，避免每次3次握手，4次挥手，和产生一堆 time_wait 状态的 socket 占用资源。</p>
<p>走tcp协议需要自己维护一些状态掉线重连，移动端网络情况复杂，有3G，4G，wifi，socket 经常断开，它断了要自动连上，并且还要对应用层透明！不能让应用层感知到！连上后数据接着之前断开的地方发送，不能有影响。</p>
<p>自动登录，这个是掉线重连后要做的一个操作，不登录拿不到股票数据。登录后接着做一个消息同步，看看有没有新消息。 </p>
<p>3、客户端100%收到（可靠性）：</p>
<p>除了 tcp 超时重传和3次ack回应保证了可靠传输之外，我们在app里也实现了一套应用层的ack机制，服务器保存了一组消息，每个消息有个seq号，一个消息推给客户端后，客户端拿到消息的seq，要发ack请求回应服务器这个消息，然后下次服务器才不会推给你，不然的话，下次服务器还是会推给你的。掉线重连，重登录后会同步一次你订阅过的消息，把上次没收到的，或者丢包的消息再同步过来。</p>
<p>4、客户端的负载均衡（这块demo中没处理）：</p>
<p>这个一般在服务器做，但后台为防止ip封锁，还有某些服务器不能在全国访问到，所以把负载均衡放在客户端做了，app启动拉取服务器列表，然后多线程并发发送测速包，得到每个服务器延迟和负载，然后选个最优的服务器连上即可，延迟可以理解为路有多长，负载可以理解为路上拥堵情况，显然最短的路不一定是最快的，它可能很堵。</p>
<p>5、多线程安全问题：</p>
<p>当多个命令发来的处理，为避免线程安全，将命令包装成task加入队列。<br>我们处理线程一共用了3个串行队列，分别对应任务数组操作队列，任务请求操作队列，数据接收操作队列。队列可以设置标识。</p>
<p>6、耗电性能等：</p>
<p>可以在进入后台时断开连接，停止心跳，停止相关操作。进入前台再自动连接开启心跳。</p>
<h6 id="设计思路"><a href="#设计思路" class="headerlink" title="设计思路"></a>设计思路</h6><p>1、大致流程 </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">graph TD</span><br><span class="line">A[开始] --&gt; B(连接)</span><br><span class="line">B --&gt; C&#123;是否连接成功&#125;</span><br><span class="line">C --&gt;|成功| D[发送心跳命令然后]</span><br><span class="line">C --&gt;|失败| E&#123;检查是否可以重连&#125;</span><br><span class="line">E --&gt;|成功| B[连接]</span><br><span class="line">E --&gt;|失败| G[结束]</span><br><span class="line">D --&gt; F[登录]</span><br><span class="line">F --&gt; H&#123;是否登录成功&#125;</span><br><span class="line">H --&gt;|成功| I[等待发送命令或接受数据]</span><br><span class="line">H --&gt;|失败| J[结束] </span><br></pre></td></tr></table></figure>



<h6 id="接口"><a href="#接口" class="headerlink" title="接口"></a>接口</h6><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">@class HHSocketManager;</span><br><span class="line">@protocol HHSocketDelegate &lt;NSObject&gt;</span><br><span class="line"> </span><br><span class="line">&#x2F;&#x2F;&#x2F; socket 收到了响应，errorCode &#x3D;&#x3D; resp.code</span><br><span class="line">- (void)socket:(GCDAsyncSocket *)socket didReceiveResponse:(HHSocketResponse *)resp;</span><br><span class="line">&#x2F;&#x2F;&#x2F; 链接</span><br><span class="line">- (void)socket:(GCDAsyncSocket *)socket didConnectToHost:(NSString *)host port:(uint16_t)port;</span><br><span class="line">&#x2F;&#x2F;&#x2F; 断开链接</span><br><span class="line">- (void)socketDidDisconnect:(GCDAsyncSocket *)socket withError:(NSError *)err;</span><br><span class="line"></span><br><span class="line">@end</span><br><span class="line"> </span><br><span class="line">@interface HHSocketManager : NSObject</span><br><span class="line"></span><br><span class="line">@property (nonatomic, weak) id &lt;HHSocketDelegate&gt; delegate;</span><br><span class="line"></span><br><span class="line">@property (nonatomic, strong, readonly) HHSocketConfiguration *configuration;</span><br><span class="line"></span><br><span class="line">@property (nonatomic, assign, readonly) HHSocketStatus socketStatus; </span><br><span class="line"></span><br><span class="line">- (instancetype)initWithConfiguration:(nonnull HHSocketConfiguration *) configuration;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;! 是否可以收发数据</span><br><span class="line">- (BOOL)avaliable;</span><br><span class="line"> </span><br><span class="line">&#x2F;&#x2F;! 断开 socket 连接</span><br><span class="line">- (void)disconnect;</span><br><span class="line">&#x2F;&#x2F;! 开始连接</span><br><span class="line">- (void)connect;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;! 发送业务请求（通过command 和 params 包装成request在发送）</span><br><span class="line">- (HHSocketRequest *)sendCommand:(UInt16)command params:(nullable NSDictionary *)params;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;! 发送请求</span><br><span class="line">- (void)sendRequest:(HHSocketRequest*)request;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F;! 取消所有请求，已发生的将无法取消</span><br><span class="line">- (void)cancelRequest:(HHSocketRequest*)request;</span><br><span class="line"> </span><br><span class="line">@end</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h5 id="demo"><a href="#demo" class="headerlink" title="demo"></a>demo</h5><p><a href="https://github.com/Baichenghui/Study/tree/master/GCDAsyncSocketManager">GCDAsyncSocketManager</a></p>
<h3 id="学习"><a href="#学习" class="headerlink" title="学习"></a>学习</h3><p><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/2dbb360886a8">iOS即时通讯，从入门到“放弃”？（这是一个系列）</a>   </p>
<p><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/edb50afa250e">通俗讲解股票类app - TCP网络通信层设计</a></p>
<p><a href="https://github.com/hehe520/AsyncSocket">AsyncSocket</a></p>
<p><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/6b870f503905">iOS websocket（SocketRocket）</a></p>

    

    

    

  </div>
  <div class="article-info article-info-index">
    
    
    

    
    <p class="article-more-link">
      <a class="article-more-a" href="/2019/10/20/iOS-%E9%95%BF%E8%BF%9E%E6%8E%A5-socket-%E8%82%A1%E7%A5%A8%E6%96%B9%E5%90%91%E7%A0%94%E7%A9%B6/">展开全文 >></a>
    </p>
    

    
    <div class="clearfix"></div>
  </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-GCD研究" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
  
  
    <h1 itemprop="name">
	
	  <a class="article-title" href="/2019/10/20/GCD%E7%A0%94%E7%A9%B6/" target="_blank">GCD研究</a>
  
    </h1>
  


  
  
<a href="/2019/10/20/GCD%E7%A0%94%E7%A9%B6/" class="archive-article-date">
        <time datetime="2019-10-20T02:52:31.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2019-10-20</time>
</a>

  
  
    

  
  </header>
  
  <div class="article-entry" itemprop="articleBody">
    
    <p>[TOC]</p>
<h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>1个进程中可以开启多条线程，每条线程可以并行（同时）执行不同的任务<br>多线程技术可以提高程序的执行效率</p>
<h5 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h5><p>显示\刷新UI界面、处理UI事件（比如点击事件、滚动事件、拖拽事件等）在主线程执行。</p>
<p>耗时操作，开启子线程执行。</p>
<h5 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h5><p>同一时间，CPU只能处理1条线程，只有1条线程在工作（执行），多线程并发（同时）执行，其实是CPU快速地在多条线程之间调度（切换），如果CPU调度线程的时间足够快，就造成了多线程并发执行的假象。</p>
<p>如果线程非常多，CPU会在N多线程之间调度，会消耗大量CPU资源，同时每条线程被调度执行的频次也会会降低（线程的执行效率降低）。<br>因此我们一般只开3-5条线程。</p>
<h5 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h5><p>能适当提高程序的执行效率<br>能适当提高资源利用率（CPU、内存利用率）</p>
<h5 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h5><p>创建线程是有开销的，iOS下主要成本包括：内核数据结构（大约1KB）、栈空间（子线程512KB、主线程1MB，也可以使用-setStackSize:设置，但必须是4K的倍数，而且最小是16K），创建线程大约需要90毫秒的创建时间<br>如果开启大量的线程，会降低程序的性能，线程越多，CPU在调度线程上的开销就越大。<br>程序设计更加复杂：比如线程之间的通信、多线程的数据共享等问题。</p>
<h3 id="多线程方案"><a href="#多线程方案" class="headerlink" title="多线程方案"></a>多线程方案</h3><p>在iOS中多线程方案有多中，但最常用的属GCD和NSOperation，然后NSThread、pthread很少使用。本文主要研究GCD这块内容。</p>
<h3 id="GCD"><a href="#GCD" class="headerlink" title="GCD"></a>GCD</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Grand Central Dispatch (GCD), contains language features, runtime libraries, and system enhancements that provide systemic, comprehensive improvements to the support for concurrent code execution on multicore hardware in macOS, iOS, watchOS, and tvOS.</span><br><span class="line"></span><br><span class="line">The BSD subsystem, Core Foundation, and Cocoa APIs have all been extended to use these enhancements to help both the system and your application to run faster, more efficiently, and with improved responsiveness. Consider how difficult it is for a single application to use multiple cores effectively, let alone to do it on different computers with different numbers of computing cores or in an environment with multiple applications competing for those cores. GCD, operating at the system level, can better accommodate the needs of all running applications, matching them to the available system resources in a balanced fashion.</span><br></pre></td></tr></table></figure>

<p>GCD全称为Grand Central Dispatch，包含语言功能，运行时库和系统增强功能，这些功能提供了系统的，全面的改进，以支持在macOS，iOS，watchOS和tvOS中的多核硬件上并发代码执行的支持。</p>
<p>BSD子系统，Core Foundation和Cocoa API均已扩展为使用这些增强功能，以帮助系统和您的应用程序更快，更高效地运行，并提高响应速度。单个应用程序有效地使用多个内核很困难，在具有不同数量计算内核的不同计算机上或在多个应用程序竞争那些内核的环境中进行操作就更困难了。在系统级别运行的GCD可以更好地满足所有正在运行的应用程序的需求，并以平衡的方式将它们与可用的系统资源进行匹配。</p>
<h5 id="队列"><a href="#队列" class="headerlink" title="队列"></a>队列</h5><h6 id="串行"><a href="#串行" class="headerlink" title="串行"></a>串行</h6><p>让任务串行执行（一个任务执行完毕后，再执行下一个任务）</p>
<h6 id="并发"><a href="#并发" class="headerlink" title="并发"></a>并发</h6><p>让多个任务并发执行（自动开启多个线程同时执行任务）<br>并发功能只有在异步（dispatch_async）函数下才有效</p>
<h6 id="dispatch-queue-set-specific"><a href="#dispatch-queue-set-specific" class="headerlink" title="dispatch_queue_set_specific"></a>dispatch_queue_set_specific</h6><p>向指定队列里面设置一个标识</p>
<p>向queue1对了中设置一个queueKey1标识:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dispatch_queue_set_specific(queue1, queueKey1, &amp;queueKey1,NULL);</span><br></pre></td></tr></table></figure>

<h6 id="dispatch-queue-get-specific"><a href="#dispatch-queue-get-specific" class="headerlink" title="dispatch_queue_get_specific"></a>dispatch_queue_get_specific</h6><p>是获取指定调度队列的上下文键/值数据。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">    const void * queueKey &#x3D; &quot;queueKey&quot;;</span><br><span class="line">    const void * queueKey2 &#x3D; &quot;queueKey2&quot;;</span><br><span class="line">    dispatch_queue_t queue &#x3D; dispatch_queue_create(queueKey, NULL);</span><br><span class="line">    dispatch_queue_t queue2 &#x3D; dispatch_queue_create(queueKey2, NULL);</span><br><span class="line">    </span><br><span class="line">    &#x2F;&#x2F;调用此方法会触发queueFunction函数，留个疑问queueFunction是在什么时候触发？</span><br><span class="line">    dispatch_queue_set_specific(queue, queueKey, &amp;queueKey, queueFunction);</span><br><span class="line">    dispatch_queue_set_specific(queue2, queueKey2, &amp;queueKey2, NULL);</span><br><span class="line">    </span><br><span class="line">    dispatch_sync(queue, ^&#123;</span><br><span class="line">        go();</span><br><span class="line">    &#125;);</span><br><span class="line">    dispatch_sync(queue2, ^&#123;</span><br><span class="line">        go();</span><br><span class="line">    &#125;);</span><br><span class="line">    </span><br><span class="line">    if (dispatch_queue_get_specific(queue, queueKey)) &#123;</span><br><span class="line">        NSLog(@&quot;__run in queue&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    &#x2F;&#x2F;main queue中找不到queueKey，所以这段Log不会触发，使用dispatch_get_specific(queueKey)的原理也一样</span><br><span class="line">    if (dispatch_queue_get_specific(dispatch_get_main_queue(), queueKey)) &#123;</span><br><span class="line">        NSLog(@&quot;__run in main queue&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">    if (dispatch_get_specific(queueKey)) &#123;</span><br><span class="line">        NSLog(@&quot;__run in main queue&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">void go() &#123;</span><br><span class="line">    &#x2F;&#x2F;使用dispatch_sync改变了当前的执行队列，所以这里可以检索到queueKey</span><br><span class="line">    if(dispatch_get_specific(&quot;queueKey&quot;)) &#123;</span><br><span class="line">        NSLog(@&quot;queue&quot;);</span><br><span class="line">    &#125; else if(dispatch_get_specific(&quot;queueKey2&quot;)) &#123;</span><br><span class="line">        NSLog(@&quot;queue2&quot;);</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">        NSLog(@&quot;main queue&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">void queueFunction() &#123;</span><br><span class="line">    NSLog(@&quot;__queueFunction&quot;);</span><br><span class="line">&#125;    </span><br></pre></td></tr></table></figure>

<h6 id="dispatch-get-specific"><a href="#dispatch-get-specific" class="headerlink" title="dispatch_get_specific"></a>dispatch_get_specific</h6><p>在当前队列中取出标识</p>
<p>注意iOS中线程和队列的关系，所有的动作都是在队列中执行的！</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">#import &lt;Foundation&#x2F;Foundation.h&gt;</span><br><span class="line"> </span><br><span class="line">int main(int argc, const char * argv[]) &#123;</span><br><span class="line">    @autoreleasepool &#123;</span><br><span class="line">        static void *queueKey1 &#x3D; &quot;queueKey1&quot;;</span><br><span class="line">        </span><br><span class="line">        dispatch_queue_t queue1 &#x3D; dispatch_queue_create(queueKey1, DISPATCH_QUEUE_SERIAL);</span><br><span class="line">        dispatch_queue_set_specific(queue1, queueKey1, &amp;queueKey1, NULL);</span><br><span class="line">        </span><br><span class="line">        NSLog(@&quot;1. 当前线程是: %@, 当前队列是: %@ 。&quot;,[NSThread currentThread],dispatch_get_current_queue());</span><br><span class="line">        </span><br><span class="line">        if (dispatch_get_specific(queueKey1)) &#123;</span><br><span class="line">            &#x2F;&#x2F;当前队列是主队列，不是queue1队列，所以取不到queueKey1对应的值，故而不执行</span><br><span class="line">            NSLog(@&quot;2. 当前线程是: %@, 当前队列是: %@ 。&quot;,[NSThread currentThread],dispatch_get_current_queue());</span><br><span class="line">            [NSThread sleepForTimeInterval:1];</span><br><span class="line">        &#125;else&#123;</span><br><span class="line">            NSLog(@&quot;3. 当前线程是: %@, 当前队列是: %@ 。&quot;,[NSThread currentThread],dispatch_get_current_queue());</span><br><span class="line">            [NSThread sleepForTimeInterval:1];</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        dispatch_sync(queue1, ^&#123;</span><br><span class="line">            NSLog(@&quot;4. 当前线程是: %@, 当前队列是: %@ 。&quot;,[NSThread currentThread],dispatch_get_current_queue());</span><br><span class="line">            [NSThread sleepForTimeInterval:1];</span><br><span class="line">            </span><br><span class="line">            if (dispatch_get_specific(queueKey1)) &#123;</span><br><span class="line">                 &#x2F;&#x2F;当前队列是queue1队列，所以能取到queueKey1对应的值，故而执行</span><br><span class="line">                NSLog(@&quot;5. 当前线程是: %@, 当前队列是: %@ 。&quot;,[NSThread currentThread],dispatch_get_current_queue());</span><br><span class="line">                [NSThread sleepForTimeInterval:1];</span><br><span class="line">            &#125;else&#123;</span><br><span class="line">                NSLog(@&quot;6. 当前线程是: %@, 当前队列是: %@ 。&quot;,[NSThread currentThread],dispatch_get_current_queue());</span><br><span class="line">                [NSThread sleepForTimeInterval:1];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        dispatch_async(queue1, ^&#123;</span><br><span class="line">            NSLog(@&quot;7. t当前线程是: %@, 当前队列是: %@ 。&quot;,[NSThread currentThread],dispatch_get_current_queue());</span><br><span class="line">            [NSThread sleepForTimeInterval:1];</span><br><span class="line">        &#125;);</span><br><span class="line">        </span><br><span class="line">        [NSThread sleepForTimeInterval:5];</span><br><span class="line">    &#125;</span><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>输出结果：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">2016-02-19 14:31:23.390 gcd[96865:820267] 1.当前线程是: &lt;NSThread: 0x1001053e0&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;,当前队列是: &lt;OS_dispatch_queue: com.apple.main-thread[0x100059ac0]&gt;。</span><br><span class="line"></span><br><span class="line">2016-02-19 14:31:23.391 gcd[96865:820267] 3.当前线程是: &lt;NSThread: 0x1001053e0&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;,当前队列是: &lt;OS_dispatch_queue: com.apple.main-thread[0x100059ac0]&gt;。</span><br><span class="line"></span><br><span class="line">2016-02-19 14:31:24.396 gcd[96865:820267] 4.当前线程是: &lt;NSThread: 0x1001053e0&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;,当前队列是: &lt;OS_dispatch_queue: queueKey1[0x103000000]&gt;。</span><br><span class="line"></span><br><span class="line">2016-02-19 14:31:25.400 gcd[96865:820267] 5.当前线程是: &lt;NSThread: 0x1001053e0&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;,当前队列是: &lt;OS_dispatch_queue: queueKey1[0x103000000]&gt;。</span><br><span class="line"></span><br><span class="line">2016-02-19 14:31:26.402 gcd[96865:820367] 7. t当前线程是: &lt;NSThread: 0x100105e10&gt;&#123;number &#x3D; 2, name &#x3D; (null)&#125;,当前队列是: &lt;OS_dispatch_queue: queueKey1[0x103000000]&gt;。</span><br><span class="line"></span><br><span class="line">Program ended with exit code: 0 </span><br></pre></td></tr></table></figure>

<h5 id="任务"><a href="#任务" class="headerlink" title="任务"></a>任务</h5><h6 id="同步"><a href="#同步" class="headerlink" title="同步"></a>同步</h6><p>在当前线程中执行任务，不具备开启新线程的能力。</p>
<p>注意：使用sync函数往当前串行队列中添加任务，会卡住当前的串行队列（产生死锁）</p>
<p>demo：</p>
<p>无论怎样都不会开启新线程</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;*</span><br><span class="line"> 同步+并发  不会开启新线程，依旧在当前线程执行任务</span><br><span class="line"> *&#x2F;</span><br><span class="line">- (void)test3 &#123;</span><br><span class="line">    dispatch_queue_t queue &#x3D; dispatch_queue_create(&quot;syncConcrrent&quot;, DISPATCH_QUEUE_CONCURRENT);</span><br><span class="line">    dispatch_sync(queue, ^&#123;</span><br><span class="line">        NSLog(@&quot;1 %@&quot;, [NSThread currentThread]);</span><br><span class="line">    &#125;);</span><br><span class="line">    </span><br><span class="line">    &#x2F;*</span><br><span class="line">     2019-12-20 16:22:30.890416+0800 threadDemo[30526:6608103] 1 &lt;NSThread: 0x600001b9c680&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     *&#x2F;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>线程死锁</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">- (void)test1 &#123;</span><br><span class="line">    &#x2F;&#x2F; dispatch_async 开启的新线程，在队列 queue 中 ，往queue队列添加同步任务执行，线程卡死</span><br><span class="line">    dispatch_queue_t queue &#x3D; dispatch_queue_create(&quot;asyncSerial&quot;, DISPATCH_QUEUE_SERIAL);</span><br><span class="line">     </span><br><span class="line">    dispatch_async(queue, ^&#123; </span><br><span class="line">        NSLog(@&quot;test1 start%@&quot;, [NSThread currentThread]);</span><br><span class="line">        &#x2F;&#x2F;Thread 3: EXC_BAD_INSTRUCTION (code&#x3D;EXC_I386_INVOP, subcode&#x3D;0x0)</span><br><span class="line">        dispatch_sync(queue, ^&#123;</span><br><span class="line">            NSLog(@&quot;test1 %@&quot;, [NSThread currentThread]);</span><br><span class="line">        &#125;);</span><br><span class="line">        </span><br><span class="line">        NSLog(@&quot;test1 end%@&quot;, [NSThread currentThread]);</span><br><span class="line">    &#125;);</span><br><span class="line">    </span><br><span class="line">    &#x2F;*</span><br><span class="line">     2019-12-20 09:56:54.932893+0800 threadDemo[11409:6405262] test1 start&lt;NSThread: 0x600002235e40&gt;&#123;number &#x3D; 5, name &#x3D; (null)&#125;</span><br><span class="line">     </span><br><span class="line">     只打印了第一条。后面出现线程死锁了。</span><br><span class="line">     </span><br><span class="line">     卡住原因：</span><br><span class="line">        1、串行队列，任务需要一个任务执行完毕接着下一个才执行</span><br><span class="line">        2、现在队列queue要执行 dispatch_sync 函数添加一个同步任务block</span><br><span class="line">        3、dispatch_sync 是同步的，需要将添加的任务block立即执行</span><br><span class="line">        4、此时调用 dispatch_sync 函数所在线程处于等待状态，需要block任务执行才继续往后执行，而执行 dispatch_sync 函数的线程与block执行所在线程是同一个线程，所以这个线程一直处于等待状态。不会往后执行，也不会执行block。</span><br><span class="line">     *&#x2F;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#96;&#96;&#96; </span><br><span class="line"></span><br><span class="line">YYKit库中应用</span><br></pre></td></tr></table></figure>
<p>/**<br> YYKit库中，在SDWebImage中也有类似的应用</p>
<p> 用于确保任务在主线程下执行<br> */<br>static inline void _yy_dispatch_sync_on_main_queue(void (^block)(void)) {<br>    if (pthread_main_np()) {<br>        block();<br>    } else {<br>        dispatch_sync(dispatch_get_main_queue(), block);<br>    }<br>}</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">AFNetworking库中应用</span><br></pre></td></tr></table></figure>
<p>/**<br> AFNetworking库中</p>
<p> AFURLSessionManager 中session创建时用到：<br>    由于session创建在iOS8之前是线程不安全的，所以使用同步+串行队列实现锁的功能</p>
<p> 注意：当前队列与同步函数中任务的队列不是一个队列<br> */<br>//    static void url_session_manager_create_task_safely(dispatch_block_t block) {<br>//        if (NSFoundationVersionNumber &lt; NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {<br>//            // Fix of bug<br>//            // Open Radar:<a target="_blank" rel="noopener" href="http://openradar.appspot.com/radar?id=5871104061079552">http://openradar.appspot.com/radar?id=5871104061079552</a> (status: Fixed in iOS8)<br>//            // Issue about:<a href="https://github.com/AFNetworking/AFNetworking/issues/2093">https://github.com/AFNetworking/AFNetworking/issues/2093</a><br>//            dispatch_sync(url_session_manager_creation_queue(), block);<br>//        } else {<br>//            block();<br>//        }<br>//    }</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">###### 异步</span><br><span class="line"></span><br><span class="line">在新的线程中执行任务，具备开启新线程的能力</span><br><span class="line"></span><br><span class="line">demo:</span><br><span class="line"></span><br><span class="line">在主队列，异步执行任务，不会开启新线程</span><br></pre></td></tr></table></figure>
<ul>
<li><p>(void)test1 {<br>  dispatch_queue_t queue = dispatch_get_main_queue();</p>
<p>  dispatch_async(queue, ^{ NSLog(@”1 %@”, [NSThread currentThread]); });<br>  dispatch_async(queue, ^{ NSLog(@”2 %@”, [NSThread currentThread]); });<br>  dispatch_async(queue, ^{ NSLog(@”3 %@”, [NSThread currentThread]); });</p>
<p>  /**</p>
<pre><code>  2019-12-18 11:50:33.358149+0800 threadDemo[4613:5504490] 1 &lt;NSThread: 0x600001cde1c0&gt;&#123;number = 1, name = main&#125;
  2019-12-18 11:50:33.358549+0800 threadDemo[4613:5504490] 2 &lt;NSThread: 0x600001cde1c0&gt;&#123;number = 1, name = main&#125;
  2019-12-18 11:50:33.358696+0800 threadDemo[4613:5504490] 3 &lt;NSThread: 0x600001cde1c0&gt;&#123;number = 1, name = main&#125;</code></pre>
<p>   */<br>}</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">除主队列外，异步执行任务，都会开启新线程</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<ul>
<li><p>(void)test2 {<br>  dispatch_queue_t queue1= dispatch_queue_create(“asyncSerial”, DISPATCH_QUEUE_SERIAL);<br>  dispatch_async(queue1, ^{ NSLog(@”1-1 %@”, [NSThread currentThread]); });<br>  dispatch_async(queue1, ^{ NSLog(@”1-2 %@”, [NSThread currentThread]); });<br>  dispatch_async(queue1, ^{ NSLog(@”1-3 %@”, [NSThread currentThread]); });</p>
<p>  /**</p>
<pre><code>  2019-12-18 11:53:59.972777+0800 threadDemo[4656:5507191] 1 &lt;NSThread: 0x6000012fd380&gt;&#123;number = 3, name = (null)&#125;
  2019-12-18 11:53:59.973284+0800 threadDemo[4656:5507191] 2 &lt;NSThread: 0x6000012fd380&gt;&#123;number = 3, name = (null)&#125;
  2019-12-18 11:53:59.973437+0800 threadDemo[4656:5507191] 3 &lt;NSThread: 0x6000012fd380&gt;&#123;number = 3, name = (null)&#125;</code></pre>
<p>   */</p>
</li>
</ul>
<p>//    dispatch_queue_t queue2 = dispatch_queue_create(“asyncConcurrent”, DISPATCH_QUEUE_CONCURRENT);<br>//    dispatch_async(queue2, ^{ NSLog(@”2-1 %@”, [NSThread currentThread]); });<br>//    dispatch_async(queue2, ^{ NSLog(@”2-2 %@”, [NSThread currentThread]); });<br>//    dispatch_async(queue2, ^{ NSLog(@”2-3 %@”, [NSThread currentThread]); });</p>
<pre><code>/**
 2019-12-18 11:57:09.136050+0800 threadDemo[4686:5509014] 2-2 &lt;NSThread: 0x600002350000&gt;&#123;number = 6, name = (null)&#125;
 2019-12-18 11:57:09.136154+0800 threadDemo[4686:5509013] 2-1 &lt;NSThread: 0x60000237bd00&gt;&#123;number = 5, name = (null)&#125;
 2019-12-18 11:57:09.136174+0800 threadDemo[4686:5509015] 2-3 &lt;NSThread: 0x60000237b480&gt;&#123;number = 4, name = (null)&#125;
 */</code></pre>
<p>}</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">##### Dispatch Group</span><br><span class="line"></span><br><span class="line">[参考](https:&#x2F;&#x2F;xiaozhuanlan.com&#x2F;topic&#x2F;0863519247)</span><br><span class="line"> </span><br><span class="line">dispatch_group可以将GCD的任务合并到一个组里来管理，也可以同时监听组里所有任务的执行情况。</span><br><span class="line">    </span><br><span class="line">###### dispatch_group_create</span><br><span class="line"></span><br><span class="line">本质是一个初始value为LONG_MAX的semaphore，通过信号量来实现一组任务的管理，代码如下：</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>dispatch_group_t dispatch_group_create(void) {<br>    //申请内存空间<br>    dispatch_group_t dg = (dispatch_group_t)_dispatch_alloc(<br>            DISPATCH_VTABLE(group), sizeof(struct dispatch_semaphore_s));<br>    //使用LONG_MAX初始化信号量结构体<br>    _dispatch_semaphore_init(LONG_MAX, dg);<br>    return dg;<br>}</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">###### dispatch_group_async</span><br><span class="line"> </span><br><span class="line">加入组的任务异步执行 </span><br><span class="line"></span><br><span class="line">###### dispatch_group_enter &#x2F; leave </span><br><span class="line"></span><br><span class="line">dispatch_group_enter的逻辑是将dispatch_group_t转换成dispatch_semaphore_t后将dsema_value的值减一。</span><br><span class="line"> </span><br><span class="line">应用注意：</span><br><span class="line">1、dispatch_group_enter必须在dispatch_group_leave之前出现</span><br><span class="line">2、dispatch_group_enter和dispatch_group_leave必须成对出现</span><br><span class="line"></span><br><span class="line">demo：</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<ul>
<li>(void)test1 {<br>  NSLog(@”1:%@”,[NSThread currentThread]);<br>  dispatch_group_t group = dispatch_group_create();<br>  dispatch_queue_t queue = dispatch_queue_create(“concurrentQueue”, DISPATCH_QUEUE_CONCURRENT);</li>
</ul>
<p>//    //注意不要放在这,而应该放在dispatch_group_enter / leave 最后面。<br>//    //否则可额能计数不对，导致提前回调<br>//    dispatch_group_notify(group, queue, ^{<br>//            NSLog(@”2”);<br>//    });</p>
<pre><code>dispatch_group_enter(group);
dispatch_async(queue, ^&#123;
    sleep(1);
    NSLog(@&quot;3:%@&quot;,[NSThread currentThread]);
    dispatch_group_leave(group);
&#125;);

dispatch_group_enter(group);
dispatch_async(queue, ^&#123;
    sleep(3);
    NSLog(@&quot;4:%@&quot;,[NSThread currentThread]);
    dispatch_group_leave(group);
&#125;);

dispatch_group_notify(group, queue, ^&#123;
        NSLog(@&quot;2:%@&quot;,[NSThread currentThread]);
&#125;);

NSLog(@&quot;5:%@&quot;,[NSThread currentThread]);

/**
2019-12-18 16:03:42.575564+0800 threadDemo[5518:5610267] 1:&lt;NSThread: 0x600001c085c0&gt;&#123;number = 1, name = main&#125;
2019-12-18 16:03:42.576226+0800 threadDemo[5518:5610267] 5:&lt;NSThread: 0x600001c085c0&gt;&#123;number = 1, name = main&#125;
2019-12-18 16:03:43.578856+0800 threadDemo[5518:5610368] 3:&lt;NSThread: 0x600001c4ffc0&gt;&#123;number = 4, name = (null)&#125;
2019-12-18 16:03:45.579819+0800 threadDemo[5518:5610366] 4:&lt;NSThread: 0x600001c4b580&gt;&#123;number = 6, name = (null)&#125;
2019-12-18 16:03:45.580037+0800 threadDemo[5518:5610366] 2:&lt;NSThread: 0x600001c4b580&gt;&#123;number = 6, name = (null)&#125;
*/</code></pre>
<p>}</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">###### dispatch_group_wait</span><br><span class="line"></span><br><span class="line">同步等待，直到group里面的block全部执行完毕，才会继续往后执行。</span><br><span class="line"></span><br><span class="line">需要注意下 dispatch_group_wait 的位置，不能放在任务添加之前。</span><br><span class="line"></span><br><span class="line">demo:</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<ul>
<li><p>(void)test3 {<br>  dispatch_queue_t aDQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);<br>  dispatch_group_t group = dispatch_group_create();<br>  // Add a task to the group<br>  dispatch_group_async(group, aDQueue, ^{</p>
<pre><code>  sleep(2);
  printf(&quot;task 1 \n&quot;);</code></pre>
<p>  });<br>  dispatch_group_async(group, aDQueue, ^{</p>
<pre><code>  printf(&quot;task 2 \n&quot;);</code></pre>
<p>  });</p>
<p>  printf(“wait 1 2 \n”);<br>  //同步等待，直到group里面的block全部执行完毕，才会继续往后执行。<br>  dispatch_group_wait(group, DISPATCH_TIME_FOREVER);<br>  printf(“task 1 2 finished \n”);</p>
<p>  /**</p>
<pre><code>  wait 1 2
  task 2
  task 1
  task 1 2 finished</code></pre>
<p>  */<br>}</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">###### dispatch_group_notify</span><br><span class="line"></span><br><span class="line">队列组拦截通知模块(内部本身是异步执行的,不会阻塞线程) </span><br><span class="line"></span><br><span class="line">需要注意下 dispatch_group_notify 的位置，不能放在任务添加之前。</span><br><span class="line"></span><br><span class="line">demo:</span><br><span class="line"></span><br></pre></td></tr></table></figure>
</li>
<li><p>(void)test2 {<br>  NSLog(@”1:%@”,[NSThread currentThread]);<br>  // 创建队列<br>  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);<br>  // 创建队列组<br>  dispatch_group_t group = dispatch_group_create();</p>
<p>  /*<br>  // 不要写在这，没意义，我们都知道他在组内所有的任务执行完毕会调用 dispatch_group_notify 中的回调块。<br>  // 但是后面还有一句，当组内没有任务时，dispatch_group_notify 中的回调块也会立即执行。<br>  // 队列组拦截通知模块(内部本身是异步执行的,不会阻塞线程)<br>  dispatch_group_notify(group, queue, ^{</p>
<pre><code>  NSLog(@&quot;4:%@&quot;,[NSThread currentThread]);</code></pre>
<p>  });</p>
<p>  2019-12-18 16:07:54.677923+0800 threadDemo[5581:5613688] 1:&lt;NSThread: 0x6000035c57c0&gt;{number = 1, name = main}<br>  2019-12-18 16:07:54.678160+0800 threadDemo[5581:5613816] 4:&lt;NSThread: 0x600003595cc0&gt;{number = 6, name = (null)}<br>  2019-12-18 16:07:55.678663+0800 threadDemo[5581:5613688] 5:&lt;NSThread: 0x6000035c57c0&gt;{number = 1, name = main}<br>  2019-12-18 16:07:56.681079+0800 threadDemo[5581:5613812] 2:&lt;NSThread: 0x6000035995c0&gt;{number = 5, name = (null)}<br>  2019-12-18 16:07:57.680645+0800 threadDemo[5581:5613815] 3:&lt;NSThread: 0x60000359f300&gt;{number = 3, name = (null)}<br>  */</p>
<p>  //队列组异步执行任务<br>  dispatch_group_async(group, queue, ^{</p>
<pre><code>  sleep(2);
  NSLog(@&quot;2:%@&quot;,[NSThread currentThread]);</code></pre>
<p>  });<br>  dispatch_group_async(group, queue, ^{</p>
<pre><code>  sleep(3);
  NSLog(@&quot;3:%@&quot;,[NSThread currentThread]);</code></pre>
<p>  });<br>  // 队列组拦截通知模块(内部本身是异步执行的,不会阻塞线程)<br>  dispatch_group_notify(group, queue, ^{</p>
<pre><code>  NSLog(@&quot;4:%@&quot;,[NSThread currentThread]);</code></pre>
<p>  });</p>
<p>  sleep(1);<br>  NSLog(@”5:%@”,[NSThread currentThread]);</p>
<p>  /*<br>   2019-12-18 16:06:55.380553+0800 threadDemo[5558:5612492] 1:&lt;NSThread: 0x600003d60800&gt;{number = 1, name = main}<br>   2019-12-18 16:06:56.381177+0800 threadDemo[5558:5612492] 5:&lt;NSThread: 0x600003d60800&gt;{number = 1, name = main}<br>   2019-12-18 16:06:57.384415+0800 threadDemo[5558:5612611] 2:&lt;NSThread: 0x600003d30380&gt;{number = 5, name = (null)}<br>   2019-12-18 16:06:58.385496+0800 threadDemo[5558:5612614] 3:&lt;NSThread: 0x600003d351c0&gt;{number = 3, name = (null)}<br>   2019-12-18 16:06:58.385737+0800 threadDemo[5558:5612614] 4:&lt;NSThread: 0x600003d351c0&gt;{number = 3, name = (null)}<br>   */<br>}</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">##### dispatch_block</span><br><span class="line"></span><br><span class="line">[dispatch_block](https:&#x2F;&#x2F;www.cnblogs.com&#x2F;KobeLuo&#x2F;p&#x2F;6464233.html)</span><br><span class="line"></span><br><span class="line">[dispatch_block](https:&#x2F;&#x2F;www.jianshu.com&#x2F;p&#x2F;5a16dfd36fad)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">##### dispatch_semaphore</span><br><span class="line"> 信号量，有三个方法：</span><br><span class="line">  </span><br><span class="line">###### dispatch_semaphore_create</span><br><span class="line"></span><br><span class="line">dispatch_semaphore_t dispatch_semaphore_create(long value);</span><br><span class="line">    创建一个信号量，信号量的值为入参 value</span><br><span class="line"></span><br><span class="line">###### dispatch_semaphore_wait</span><br><span class="line"></span><br><span class="line">long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);</span><br><span class="line">    接收一个信号和时间值，若信号的信号量为0，则会阻塞当前线程，直到信号量大于0或者经过输入的时间值；</span><br><span class="line">    若信号量大于0，则会使信号量减1并返回，程序继续住下执行</span><br><span class="line"></span><br><span class="line">###### dispatch_semaphore_signal</span><br><span class="line"></span><br><span class="line">long dispatch_semaphore_signal(dispatch_semaphore_t dsema);</span><br><span class="line">    接收一个信号量，发送信号使信号量的值 +1并返回</span><br><span class="line"> </span><br><span class="line">###### 使用</span><br><span class="line"></span><br><span class="line">应用场景：</span><br><span class="line"></span><br><span class="line">1. 充当锁的功能</span><br><span class="line">2. 异步任务，同步返回（同步获取指定APP在AppStore中的当前版本）</span><br><span class="line">3. 并发控制（实现与NSOperationQueue中max-ConcurrentOperationCount 类似功能。）</span><br><span class="line"></span><br><span class="line">demo:</span><br><span class="line"></span><br><span class="line">并发控制</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>@implementation SemaphoreMaxConcurrentCount {<br>  dispatch_semaphore_t _semaphore;<br>  dispatch_queue_t _queue;<br>}</p>
</li>
<li><p>(instancetype)init {<br>  return [self initWithMaxConcurrentCount:3];;<br>}</p>
</li>
<li><p>(instancetype)initWithMaxConcurrentCount:(NSInteger)count {<br>  if (self = [super init]) {</p>
<pre><code>  if (count &lt; 1) &#123;
      count = 3;
  &#125;
  _semaphore = dispatch_semaphore_create(count);
  _queue = dispatch_queue_create(&quot;asyncConcurrent&quot;, DISPATCH_QUEUE_CONCURRENT);</code></pre>
<p>  }<br>  return self;<br>}</p>
</li>
</ul>
<ul>
<li>(void)addTask:(TaskBlock)block {<br>  dispatch_async(_queue, ^{<pre><code>  dispatch_semaphore_wait(self-&gt;_semaphore, DISPATCH_TIME_FOREVER);
  dispatch_async(dispatch_get_global_queue(0, 0), ^&#123;
      block();
      dispatch_semaphore_signal(self-&gt;_semaphore);
  &#125;);</code></pre>
  });<br>}</li>
</ul>
<p>@end</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">AFNetworking库中</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>/**<br> AFNetworking库中</p>
<p> 获取session中完成的tasks。这个 与 <code>appVersionInAppStore:</code> 方法中的作用差不多。</p>
<p> 都是通过block获取数据，然后将数据直接返回出去，避免了其他地方获取task也要用回调的方式获取数据。</p>
<p> */<br>//- (NSArray *)tasksForKeyPath:(NSString *)keyPath {<br>//    __block NSArray *tasks = nil;<br>//    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);<br>//    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {<br>//        if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {<br>//            tasks = dataTasks;<br>//        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {<br>//            tasks = uploadTasks;<br>//        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {<br>//            tasks = downloadTasks;<br>//        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {<br>//            tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@”@unionOfArrays.self”];<br>//        }<br>//<br>//        dispatch_semaphore_signal(semaphore);<br>//    }];<br>//<br>//    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);<br>//<br>//    return tasks;<br>//}</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">充当锁的功能</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>/**<br> 充当锁的功能</p>
<p> 当线程1执行到dispatch_semaphore_wait这一行时，semaphore的信号量为1，所以使信号量-1变为0，并且线程1继续往下执行；<br> 当在线程1NSLog这一行代码还没执行完的时候，又有线程2来访问，执行dispatch_semaphore_wait时由于此时信号量为0，且时间为DISPATCH_TIME_FOREVER,所以会一直阻塞线程2（此时线程2处于等待状态），直到线程1执行完NSLog并执行完dispatch_semaphore_signal使信号量为1后，线程2才能解除阻塞继续住下执行。<br> 这就可以保证同时只有一个线程执行NSLog这一行代码。</p>
<p> */</p>
<ul>
<li><p>(void)test1 {<br>  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);<br>  dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);<br>  for (int i = 0; i &lt; 100; i++) {</p>
<pre><code>  dispatch_async(queue, ^&#123;
      // 相当于加锁
      dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
      NSLog(@&quot;i = %d semaphore = %@&quot;, i, semaphore);
      // 相当于解锁
      dispatch_semaphore_signal(semaphore);
  &#125;);</code></pre>
<p>  }<br>}</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">异步任务，同步返回(同步获取指定APP在AppStore中的当前版本)</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>/**<br>异步任务，同步返回</p>
<p>同步获取指定APP在AppStore中的当前版本<br>*/</p>
</li>
<li><p>(NSString *)appVersionInAppStore:(NSString *)appId {<br>  __block NSString *appVersion = @””;<br>  NSString *url = [NSString stringWithFormat:@”<a target="_blank" rel="noopener" href="https://itunes.apple.com/lookup?id=%25@&quot;,appId%5D">https://itunes.apple.com/lookup?id=%@&quot;,appId]</a>;<br>  NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];</p>
<p>  dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);</p>
<p>  NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {</p>
<pre><code>  NSError *err;
  NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingMutableContainers) error:&amp;err];
  if (!err) &#123;
      NSArray *results = jsonData[@&quot;results&quot;];
      if ([results isKindOfClass:[NSArray class]] &amp;&amp; results != nil &amp;&amp; results.count &gt; 0) &#123;
          appVersion = results.firstObject[@&quot;version&quot;];
      &#125;
  &#125;

  dispatch_semaphore_signal(semaphore);</code></pre>
<p>  }];</p>
<p>  [dataTask resume];</p>
<p>  dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);</p>
<p>  return appVersion;</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">##### dispatch_barrier</span><br><span class="line"> </span><br><span class="line">在一个并行队列中，有多个线程在执行多个任务，在这个并行队列中，有一个dispatch_barrier任务。这样会使所有在这个dispatch_barrier之后的任务总会等待barrier之前的所有任务结束之后，才会执行。</span><br><span class="line"></span><br><span class="line">dispatch_barrier 又分为 dispatch_barrier_sync 和 dispatch_barrier_async</span><br><span class="line"></span><br><span class="line">注意：</span><br><span class="line">1.barrier和串行队列配合是完全没有意义的。</span><br><span class="line">barrier的目的是为了在某种情况下，同一个队列中一些并发任务必须在另一些并发任务之后执行，所以需要一个类似于拦截的功能，迫使后执”</span><br><span class="line">“行的任务必须等待。那么，串行队列中的所有任务本身就是按照顺序执行的。</span><br><span class="line"></span><br><span class="line">2.在global queue中使用barrier没有意义。</span><br><span class="line">barrier实现的基本条件是，要写在同一队列中。举个例子，你现在创建了两个并行队列，你在其中一个队列中插入了一个barrier任务，那么你不可能期待他可以在第二个队列中生效，对吧。同样的，每一次使用global queue，系统分配给你的可能是不同的并行队列，你在其中插入一个barrier任务，没有意义。</span><br><span class="line"></span><br><span class="line">###### dispatch_barrier_sync</span><br><span class="line"></span><br><span class="line">dispatch_barrier_sync 中的任务同步执行，会阻塞当前线程</span><br><span class="line"></span><br><span class="line">demo:</span><br><span class="line"></span><br></pre></td></tr></table></figure></li>
<li><p>(void)test1 {<br>  dispatch_queue_t queue = dispatch_queue_create(“asyncConcurrent”, DISPATCH_QUEUE_CONCURRENT);</p>
<p>  for (int i = 0; i &lt; 10; i++) {</p>
<pre><code>  if (i % 2 == 0) &#123;
      dispatch_async(queue, ^&#123;
          sleep(1);
          NSLog(@&quot;dispatch_barrier_sync 之前的任务:%d&quot;,i);
      &#125;);
  &#125;
  else &#123;
      dispatch_async(queue, ^&#123;
          NSLog(@&quot;dispatch_barrier_sync 之前的任务:%d&quot;,i);
      &#125;);
  &#125;</code></pre>
<p>  }</p>
<p>  dispatch_barrier_sync(queue, ^{</p>
<pre><code>  NSLog(@&quot;dispatch_barrier_sync 任务执行 %@&quot;, [NSThread currentThread]);</code></pre>
<p>  });</p>
<p>  NSLog(@”dispatch_barrier_sync 所在线程 %@”, [NSThread currentThread]);</p>
<p>  for (int i = 0; i &lt; 10; i++) {</p>
<pre><code>  if (i % 2 == 0) &#123;
      dispatch_async(queue, ^&#123;
          sleep(1);
          NSLog(@&quot;dispatch_barrier_sync 之后的任务:%d&quot;,i);
      &#125;);
  &#125;
  else &#123;
      dispatch_async(queue, ^&#123;
          NSLog(@&quot;dispatch_barrier_sync 之后的任务:%d&quot;,i);
      &#125;);
  &#125;</code></pre>
<p>  }</p>
<p>  /**<br>   2019-12-19 13:41:20.335598+0800 threadDemo[8655:6043711] dispatch_barrier_sync 之前的任务:1<br>   2019-12-19 13:41:20.335702+0800 threadDemo[8655:6044001] dispatch_barrier_sync 之前的任务:3<br>   2019-12-19 13:41:20.336030+0800 threadDemo[8655:6044003] dispatch_barrier_sync 之前的任务:5<br>   2019-12-19 13:41:20.336205+0800 threadDemo[8655:6044004] dispatch_barrier_sync 之前的任务:7<br>   2019-12-19 13:41:20.336373+0800 threadDemo[8655:6044005] dispatch_barrier_sync 之前的任务:9<br>   2019-12-19 13:41:21.340593+0800 threadDemo[8655:6043711] dispatch_barrier_sync 之前的任务:6<br>   2019-12-19 13:41:21.340593+0800 threadDemo[8655:6043998] dispatch_barrier_sync 之前的任务:0<br>   2019-12-19 13:41:21.340593+0800 threadDemo[8655:6044001] dispatch_barrier_sync 之前的任务:8<br>   2019-12-19 13:41:21.340593+0800 threadDemo[8655:6044002] dispatch_barrier_sync 之前的任务:4<br>   2019-12-19 13:41:21.340636+0800 threadDemo[8655:6044000] dispatch_barrier_sync 之前的任务:2<br>   2019-12-19 13:41:21.340931+0800 threadDemo[8655:6043503] dispatch_barrier_sync 任务执行 &lt;NSThread: 0x6000014820c0&gt;{number = 1, name = main}<br>   2019-12-19 13:41:21.341159+0800 threadDemo[8655:6043503] dispatch_barrier_sync 所在线程 &lt;NSThread: 0x6000014820c0&gt;{number = 1, name = main}<br>   2019-12-19 13:41:21.341339+0800 threadDemo[8655:6044002] dispatch_barrier_sync 之后的任务:1<br>   2019-12-19 13:41:21.341552+0800 threadDemo[8655:6043998] dispatch_barrier_sync 之后的任务:3<br>   2019-12-19 13:41:21.342288+0800 threadDemo[8655:6044002] dispatch_barrier_sync 之后的任务:5<br>   2019-12-19 13:41:21.342807+0800 threadDemo[8655:6044004] dispatch_barrier_sync 之后的任务:7<br>   2019-12-19 13:41:21.342890+0800 threadDemo[8655:6043711] dispatch_barrier_sync 之后的任务:9<br>   2019-12-19 13:41:22.345189+0800 threadDemo[8655:6044001] dispatch_barrier_sync 之后的任务:2<br>   2019-12-19 13:41:22.345189+0800 threadDemo[8655:6044005] dispatch_barrier_sync 之后的任务:4<br>   2019-12-19 13:41:22.345227+0800 threadDemo[8655:6044003] dispatch_barrier_sync 之后的任务:6<br>   2019-12-19 13:41:22.345227+0800 threadDemo[8655:6044000] dispatch_barrier_sync 之后的任务:0<br>   2019-12-19 13:41:22.345237+0800 threadDemo[8655:6043998] dispatch_barrier_sync 之后的任务:8<br>   */<br>}</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">###### dispatch_barrier_async</span><br><span class="line"> </span><br><span class="line">dispatch_barrier_async 中的任务异步执行，不会阻塞当前线程</span><br><span class="line"></span><br><span class="line">demo:</span><br><span class="line"></span><br><span class="line">&#96;&#96;&#96; </span><br><span class="line">- (void)test2 &#123;</span><br><span class="line">    dispatch_queue_t queue &#x3D; dispatch_queue_create(&quot;asyncConcurrent&quot;, DISPATCH_QUEUE_CONCURRENT);</span><br><span class="line">    </span><br><span class="line">    for (int i &#x3D; 0; i &lt; 10; i++) &#123;</span><br><span class="line">        if (i % 2 &#x3D;&#x3D; 0) &#123;</span><br><span class="line">            dispatch_async(queue, ^&#123;</span><br><span class="line">                sleep(1);</span><br><span class="line">                NSLog(@&quot;dispatch_barrier_sync 之前的任务:%d&quot;,i);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">        else &#123;</span><br><span class="line">            dispatch_async(queue, ^&#123;</span><br><span class="line">                NSLog(@&quot;dispatch_barrier_sync 之前的任务:%d&quot;,i);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    dispatch_barrier_async(queue, ^&#123;</span><br><span class="line">        NSLog(@&quot;dispatch_barrier_sync 任务执行 %@&quot;, [NSThread currentThread]);</span><br><span class="line">    &#125;);</span><br><span class="line">    </span><br><span class="line">    NSLog(@&quot;dispatch_barrier_async 所在线程 %@&quot;, [NSThread currentThread]);</span><br><span class="line">    </span><br><span class="line">    for (int i &#x3D; 0; i &lt; 10; i++) &#123;</span><br><span class="line">        if (i % 2 &#x3D;&#x3D; 0) &#123;</span><br><span class="line">            dispatch_async(queue, ^&#123;</span><br><span class="line">                sleep(1);</span><br><span class="line">                NSLog(@&quot;dispatch_barrier_sync 之后的任务:%d&quot;,i);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">        else &#123;</span><br><span class="line">            dispatch_async(queue, ^&#123;</span><br><span class="line">                NSLog(@&quot;dispatch_barrier_sync 之后的任务:%d&quot;,i);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    &#x2F;**</span><br><span class="line">     2019-12-19 13:40:48.135837+0800 threadDemo[8655:6043503] dispatch_barrier_async 所在线程 &lt;NSThread: 0x6000014820c0&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 13:40:48.137458+0800 threadDemo[8655:6043615] dispatch_barrier_sync 之前的任务:1</span><br><span class="line">     2019-12-19 13:40:48.152424+0800 threadDemo[8655:6043615] dispatch_barrier_sync 之前的任务:3</span><br><span class="line">     2019-12-19 13:40:48.152613+0800 threadDemo[8655:6043615] dispatch_barrier_sync 之前的任务:5</span><br><span class="line">     2019-12-19 13:40:48.152732+0800 threadDemo[8655:6043711] dispatch_barrier_sync 之前的任务:7</span><br><span class="line">     2019-12-19 13:40:48.152829+0800 threadDemo[8655:6043615] dispatch_barrier_sync 之前的任务:9</span><br><span class="line">     2019-12-19 13:40:49.140811+0800 threadDemo[8655:6043611] dispatch_barrier_sync 之前的任务:0</span><br><span class="line">     2019-12-19 13:40:49.153305+0800 threadDemo[8655:6043710] dispatch_barrier_sync 之前的任务:6</span><br><span class="line">     2019-12-19 13:40:49.153305+0800 threadDemo[8655:6043609] dispatch_barrier_sync 之前的任务:2</span><br><span class="line">     2019-12-19 13:40:49.153305+0800 threadDemo[8655:6043709] dispatch_barrier_sync 之前的任务:4</span><br><span class="line">     2019-12-19 13:40:49.153359+0800 threadDemo[8655:6043712] dispatch_barrier_sync 之前的任务:8</span><br><span class="line">     2019-12-19 13:40:49.153667+0800 threadDemo[8655:6043712] dispatch_barrier_sync 任务执行 &lt;NSThread: 0x60000140dd00&gt;&#123;number &#x3D; 7, name &#x3D; (null)&#125;</span><br><span class="line">     2019-12-19 13:40:49.153994+0800 threadDemo[8655:6043712] dispatch_barrier_sync 之后的任务:1</span><br><span class="line">     2019-12-19 13:40:49.154061+0800 threadDemo[8655:6043710] dispatch_barrier_sync 之后的任务:3</span><br><span class="line">     2019-12-19 13:40:49.154194+0800 threadDemo[8655:6043710] dispatch_barrier_sync 之后的任务:5</span><br><span class="line">     2019-12-19 13:40:49.154419+0800 threadDemo[8655:6043714] dispatch_barrier_sync 之后的任务:7</span><br><span class="line">     2019-12-19 13:40:49.154460+0800 threadDemo[8655:6043713] dispatch_barrier_sync 之后的任务:9</span><br><span class="line">     2019-12-19 13:40:50.154831+0800 threadDemo[8655:6043709] dispatch_barrier_sync 之后的任务:0</span><br><span class="line">     2019-12-19 13:40:50.154831+0800 threadDemo[8655:6043611] dispatch_barrier_sync 之后的任务:4</span><br><span class="line">     2019-12-19 13:40:50.154869+0800 threadDemo[8655:6043712] dispatch_barrier_sync 之后的任务:6</span><br><span class="line">     2019-12-19 13:40:50.154869+0800 threadDemo[8655:6043615] dispatch_barrier_sync 之后的任务:8</span><br><span class="line">     2019-12-19 13:40:50.154867+0800 threadDemo[8655:6043609] dispatch_barrier_sync 之后的任务:2</span><br><span class="line">     </span><br><span class="line">     *&#x2F;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

</li>
</ul>
<h5 id="dispatch-once"><a href="#dispatch-once" class="headerlink" title="dispatch_once"></a>dispatch_once</h5><p>dispatch_once能保证任务只会被执行一次，同时多线程调用也是线程安全的。</p>
<p>原理：dispatch_once用原子性操作block执行完成标记位，同时用信号量确保只有一个线程执行block，等block执行完再唤醒所有等待中的线程。</p>
<p>应用场景：</p>
<ol>
<li>dispatch_once 常被用于创建单例</li>
<li>只需执行一次的函数都可以使用</li>
</ol>
<p>demo:</p>
<p>AFNetworking 库中 </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;*</span><br><span class="line"> AFNetworking 库中创建队列</span><br><span class="line"> 1、用到时才创建</span><br><span class="line"> 2、确保只要创建一次</span><br><span class="line"> 3、需要线程安全</span><br><span class="line"> 综上原因，使用 dispatch_once 是最优选择</span><br><span class="line"> </span><br><span class="line"> *&#x2F;</span><br><span class="line">&#x2F;&#x2F;static dispatch_queue_t url_session_manager_processing_queue() &#123;</span><br><span class="line">&#x2F;&#x2F;    static dispatch_queue_t af_url_session_manager_processing_queue;</span><br><span class="line">&#x2F;&#x2F;    static dispatch_once_t onceToken;</span><br><span class="line">&#x2F;&#x2F;    dispatch_once(&amp;onceToken, ^&#123;</span><br><span class="line">&#x2F;&#x2F;        af_url_session_manager_processing_queue &#x3D; dispatch_queue_create(&quot;com.alamofire.networking.session.manager.processing&quot;, DISPATCH_QUEUE_CONCURRENT);</span><br><span class="line">&#x2F;&#x2F;    &#125;);</span><br><span class="line">&#x2F;&#x2F;</span><br><span class="line">&#x2F;&#x2F;    return af_url_session_manager_processing_queue;</span><br><span class="line">&#x2F;&#x2F;&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>单利</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">- (void)test1 &#123;</span><br><span class="line">    TDManager *manager1 &#x3D; [TDManager shareManager];</span><br><span class="line">    TDManager *manager2 &#x3D; [TDManager new];</span><br><span class="line">    TDManager *manager3 &#x3D; [[TDManager alloc] init];</span><br><span class="line">    TDManager *manager4 &#x3D; [manager1 copy];</span><br><span class="line">    TDManager *manager5 &#x3D; [manager1 mutableCopy];</span><br><span class="line">    </span><br><span class="line">    NSLog(@&quot;manager1:%p&quot;,manager1);</span><br><span class="line">    NSLog(@&quot;manager2:%p&quot;,manager2);</span><br><span class="line">    NSLog(@&quot;manager3:%p&quot;,manager3);</span><br><span class="line">    NSLog(@&quot;manager4:%p&quot;,manager4);</span><br><span class="line">    NSLog(@&quot;manager5:%p&quot;,manager5);</span><br><span class="line">    </span><br><span class="line">    &#x2F;*</span><br><span class="line">     打印内存地址都一致，起到了单利效果</span><br><span class="line">     </span><br><span class="line">     2019-12-20 17:00:15.974273+0800 threadDemo[30701:6628246] manager1:0x6000021d68b0</span><br><span class="line">     2019-12-20 17:00:15.974428+0800 threadDemo[30701:6628246] manager2:0x6000021d68b0</span><br><span class="line">     2019-12-20 17:00:15.974544+0800 threadDemo[30701:6628246] manager3:0x6000021d68b0</span><br><span class="line">     2019-12-20 17:00:15.974634+0800 threadDemo[30701:6628246] manager4:0x6000021d68b0</span><br><span class="line">     2019-12-20 17:00:15.974714+0800 threadDemo[30701:6628246] manager5:0x6000021d68b0</span><br><span class="line">     *&#x2F;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="dispatch-apply"><a href="#dispatch-apply" class="headerlink" title="dispatch_apply"></a>dispatch_apply</h5><p>dispatch_apply类似一个for循环，会在指定的dispatch queue中运行block任务n次.<br>    如果队列是并发队列，则会并发执行block任务;<br>    如果队列是串行队列，则会串行在当前队列执行block任务;<br>    dispatch_apply是一个同步调用，block任务执行n次后才返回。</p>
<p>demo:</p>
<p>串行队列中执行</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;&#x2F; 串行队列中执行,效率还是略高于普通的 for 循环</span><br><span class="line">- (void)test1 &#123;</span><br><span class="line">    NSLog(@&quot;start&quot;);</span><br><span class="line">      </span><br><span class="line">    dispatch_queue_t queue&#x3D; dispatch_queue_create(&quot;asyncSerial&quot;, DISPATCH_QUEUE_SERIAL);</span><br><span class="line">&#x2F;&#x2F;    dispatch_queue_t queue &#x3D; dispatch_queue_create(&quot;asyncConcurrent&quot;, DISPATCH_QUEUE_CONCURRENT);</span><br><span class="line">    </span><br><span class="line">    CFTimeInterval startTimeInterval &#x3D; CACurrentMediaTime();</span><br><span class="line">    &#x2F;&#x2F;dispatch_apply是一个同步调用，block任务执行都执行完才返回，会卡住当前线程（无论串行还是并发队列）</span><br><span class="line">    dispatch_apply(10000, queue, ^(size_t i) &#123;</span><br><span class="line">        </span><br><span class="line">&#x2F;&#x2F;        [NSThread sleepForTimeInterval:arc4random()%1];</span><br><span class="line">        </span><br><span class="line">        NSLog(@&quot;%zu %@&quot;,i,[NSThread currentThread]);</span><br><span class="line">    &#125;);</span><br><span class="line">    CFTimeInterval endTimeInterval &#x3D; CACurrentMediaTime();</span><br><span class="line">    NSLog(@&quot;end&quot;);</span><br><span class="line">    </span><br><span class="line">    NSLog(@&quot;endTimeInterval - startTimeInterval:%f&quot;,endTimeInterval - startTimeInterval);</span><br><span class="line">    </span><br><span class="line">    &#x2F;*</span><br><span class="line">     2019-12-19 15:46:19.281002+0800 threadDemo[9336:6115616] 0 &lt;NSThread: 0x6000008aa0c0&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 15:46:19.281440+0800 threadDemo[9336:6115616] 1 &lt;NSThread: 0x6000008aa0c0&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 15:46:19.281440+0800 threadDemo[9336:6115616] 2 &lt;NSThread: 0x6000008aa0c0&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 15:46:19.281440+0800 threadDemo[9336:6115616] 3 &lt;NSThread: 0x6000008aa0c0&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     ....</span><br><span class="line">     2019-12-19 15:46:19.281002+0800 threadDemo[9336:6115616] 9997 &lt;NSThread: 0x6000008aa0c0&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 15:46:19.281440+0800 threadDemo[9336:6115616] 9998 &lt;NSThread: 0x6000008aa0c0&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 15:46:19.282302+0800 threadDemo[9336:6115616] 9999 &lt;NSThread: 0x6000008aa0c0&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 15:46:19.282819+0800 threadDemo[9336:6115616] end</span><br><span class="line">     2019-12-19 15:46:19.283203+0800 threadDemo[9336:6115616] endTimeInterval - startTimeInterval:4.206515</span><br><span class="line">     </span><br><span class="line">     *&#x2F;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br></pre></td></tr></table></figure>

<p>并发队列执行</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F;&#x2F; 并发队列中执行，比串行队列效率高</span><br><span class="line">- (void)test2 &#123;</span><br><span class="line">    NSLog(@&quot;start&quot;);</span><br><span class="line">    </span><br><span class="line">    dispatch_queue_t queue &#x3D; dispatch_queue_create(&quot;asyncConcurrent&quot;, DISPATCH_QUEUE_CONCURRENT);</span><br><span class="line">    </span><br><span class="line">    CFTimeInterval startTimeInterval &#x3D; CACurrentMediaTime();</span><br><span class="line">    &#x2F;&#x2F;dispatch_apply是一个同步调用，block任务执行都执行完才返回，会卡住当前线程（无论串行还是并发队列）</span><br><span class="line">    dispatch_apply(10000, queue, ^(size_t i) &#123;</span><br><span class="line">        </span><br><span class="line">&#x2F;&#x2F;        [NSThread sleepForTimeInterval:arc4random()%1];</span><br><span class="line">        </span><br><span class="line">        NSLog(@&quot;%zu %@&quot;,i,[NSThread currentThread]);</span><br><span class="line">    &#125;);</span><br><span class="line">    CFTimeInterval endTimeInterval &#x3D; CACurrentMediaTime();</span><br><span class="line">    NSLog(@&quot;end&quot;);</span><br><span class="line">    </span><br><span class="line">    NSLog(@&quot;endTimeInterval - startTimeInterval:%f&quot;,endTimeInterval - startTimeInterval);</span><br><span class="line">    </span><br><span class="line">    &#x2F;*</span><br><span class="line"></span><br><span class="line">     2019-12-19 16:23:13.700098+0800 threadDemo[9717:6141471] 0 &lt;NSThread: 0x600002f5a140&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 16:23:13.700240+0800 threadDemo[9717:6141471] 1 &lt;NSThread: 0x600002f5a140&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 16:23:13.700767+0800 threadDemo[9717:6141471] 2 &lt;NSThread: 0x600002f5a140&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     ...</span><br><span class="line">     2019-12-19 16:23:17.318679+0800 threadDemo[9717:6141471] 9995 &lt;NSThread: 0x600002f5a140&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 16:23:17.318908+0800 threadDemo[9717:6141715] 9996 &lt;NSThread: 0x600002fd6900&gt;&#123;number &#x3D; 8, name &#x3D; (null)&#125;</span><br><span class="line">     2019-12-19 16:23:17.319090+0800 threadDemo[9717:6141471] 9997 &lt;NSThread: 0x600002f5a140&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 16:23:17.319294+0800 threadDemo[9717:6141714] 9998 &lt;NSThread: 0x600002fd6780&gt;&#123;number &#x3D; 7, name &#x3D; (null)&#125;</span><br><span class="line">     2019-12-19 16:23:17.319498+0800 threadDemo[9717:6141662] 9999 &lt;NSThread: 0x600002fd6740&gt;&#123;number &#x3D; 6, name &#x3D; (null)&#125;</span><br><span class="line">     2019-12-19 16:23:17.321036+0800 threadDemo[9717:6141471] end</span><br><span class="line">     2019-12-19 16:23:17.321507+0800 threadDemo[9717:6141471] endTimeInterval - startTimeInterval:3.620898</span><br><span class="line">     *&#x2F;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="dispatch-after"><a href="#dispatch-after" class="headerlink" title="dispatch_after"></a>dispatch_after</h5><p>延时函数，不会卡住所在线程</p>
<p>demo:</p>
<p>任务执行所在队列为主队列，任务在主线程中执行的</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">- (void)test1 &#123;</span><br><span class="line">    NSLog(@&quot;1 %@&quot;,[NSThread currentThread]);</span><br><span class="line">    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^&#123;</span><br><span class="line">        NSLog(@&quot;2 %@&quot;,[NSThread currentThread]);</span><br><span class="line">    &#125;);</span><br><span class="line">    NSLog(@&quot;3 %@&quot;,[NSThread currentThread]);</span><br><span class="line">    </span><br><span class="line">    &#x2F;*</span><br><span class="line">     2019-12-19 14:58:46.529442+0800 threadDemo[8900:6083577] 1 &lt;NSThread: 0x6000036b8b80&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 14:58:46.529659+0800 threadDemo[8900:6083577] 3 &lt;NSThread: 0x6000036b8b80&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 14:58:48.529967+0800 threadDemo[8900:6083577] 2 &lt;NSThread: 0x6000036b8b80&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line"></span><br><span class="line">     *&#x2F;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>修改任务执行所在队列为并发队列. 任务是在子线程中执行的.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">- (void)test2 &#123;</span><br><span class="line">    NSLog(@&quot;1 %@&quot;,[NSThread currentThread]);</span><br><span class="line">    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^&#123;</span><br><span class="line">        NSLog(@&quot;2 %@&quot;,[NSThread currentThread]);</span><br><span class="line">    &#125;);</span><br><span class="line">    NSLog(@&quot;3 %@&quot;,[NSThread currentThread]);</span><br><span class="line">    </span><br><span class="line">    &#x2F;*</span><br><span class="line">     2019-12-19 15:00:48.693679+0800 threadDemo[8943:6085531] 1 &lt;NSThread: 0x600000d72ac0&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 15:00:48.694252+0800 threadDemo[8943:6085531] 3 &lt;NSThread: 0x600000d72ac0&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 15:00:50.855673+0800 threadDemo[8943:6085641] 2 &lt;NSThread: 0x600000d21080&gt;&#123;number &#x3D; 3, name &#x3D; (null)&#125;</span><br><span class="line"></span><br><span class="line">     *&#x2F;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>修改任务执行所在队列为自定义的串行队列. 任务是在子线程中执行的.<br>通过查看打印结果，很明显延时函数底层肯定是异步执行任务。只有在主队列时任务才在主线程执行</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">- (void)test3 &#123;</span><br><span class="line">    NSLog(@&quot;1 %@&quot;,[NSThread currentThread]);</span><br><span class="line">    dispatch_queue_t queue &#x3D; dispatch_queue_create(&quot;syncConcrrent&quot;, DISPATCH_QUEUE_CONCURRENT);</span><br><span class="line">    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), queue, ^&#123;</span><br><span class="line">        NSLog(@&quot;2 %@&quot;,[NSThread currentThread]);</span><br><span class="line">    &#125;);</span><br><span class="line">    NSLog(@&quot;3 %@&quot;,[NSThread currentThread]);</span><br><span class="line">    </span><br><span class="line">    &#x2F;*</span><br><span class="line">     2019-12-19 15:09:06.207532+0800 threadDemo[9037:6090879] 1 &lt;NSThread: 0x600001960f40&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 15:09:06.207745+0800 threadDemo[9037:6090879] 3 &lt;NSThread: 0x600001960f40&gt;&#123;number &#x3D; 1, name &#x3D; main&#125;</span><br><span class="line">     2019-12-19 15:09:08.207879+0800 threadDemo[9037:6090962] 2 &lt;NSThread: 0x6000019ee4c0&gt;&#123;number &#x3D; 8, name &#x3D; (null)&#125;</span><br><span class="line">     *&#x2F;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="Dispatch-Source"><a href="#Dispatch-Source" class="headerlink" title="Dispatch Source"></a>Dispatch Source</h5><p>Dispatch Source是BSD系统内核惯有功能kqueue的包装，kqueue是在XNU内核中发生各种事件时，在应用程序编程方执行处理的技术。<br> 它的CPU负荷非常小，尽量不占用资源。当事件发生时，Dispatch Source会在指定的Dispatch Queue中执行事件的处理。</p>
<p> dispatch_source_t<br> 一共有一下几种类型（dispatch_source_type_t):</p>
<p> 监控进程：DISPATCH_SOURCE_TYPE_PROC,<br> 定时器：DISPATCH_SOURCE_TYPE_TIMER,<br> 从描述符中读取数据：DISPATCH_SOURCE_TYPE_READ,<br> 向描述符中写入字符：DISPATCH_SOURCE_TYPE_WRITE,<br> 监控文件系统对象:DISPATCH_SOURCE_TYPE_VNODE,…..</p>
<p> demo演示定时器使用</p>
<p> Dispatch Source使用最多的就是用来实现定时器，source创建后默认是暂停状态，需要手动调用dispatch_resume启动定时器。dispatch_after只是封装调用了dispatch source定时器，然后在回调函数中执行定义的block。</p>
<p> Dispatch Source定时器使用时也有一些需要注意的地方，不然很可能会引起crash：</p>
<p> 1、循环引用：因为dispatch_source_set_event_handler回调是个block，在添加到source的链表上时会执行copy并被source强引用，如果block里持有了self，self又持有了source的话，就会引起循环引用。正确的方法是使用weak+strong或者提前调用dispatch_source_cancel取消timer。</p>
<p> 2、dispatch_resume和dispatch_suspend调用次数需要平衡，如果重复调用dispatch_resume则会崩溃,因为重复调用会让dispatch_resume代码里if分支不成立，从而执行了DISPATCH_CLIENT_CRASH(“Over-resume of an object”)导致崩溃。</p>
<p> 3、source在suspend状态下，如果直接设置source = nil或者重新创建source都会造成crash。正确的方式是在resume状态下调用dispatch_source_cancel(source)后再重新创建。</p>
<h6 id="GCD定时器"><a href="#GCD定时器" class="headerlink" title="GCD定时器"></a>GCD定时器</h6><p>定时器不受runloop影响，效率高。</p>
<p>demo:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">- (void)test1 &#123;</span><br><span class="line">    __block int timeout&#x3D;30; &#x2F;&#x2F;倒计时时间</span><br><span class="line">    dispatch_source_t _timer &#x3D; dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());</span><br><span class="line">    dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, 0), 1 * NSEC_PER_SEC, 0);</span><br><span class="line">    dispatch_source_set_event_handler(_timer, ^&#123;</span><br><span class="line">        if(timeout&lt;&#x3D;0)&#123; &#x2F;&#x2F;倒计时结束，关闭</span><br><span class="line">            dispatch_source_cancel(_timer);</span><br><span class="line">            dispatch_async(dispatch_get_main_queue(), ^&#123;</span><br><span class="line">                &#x2F;&#x2F;设置界面的按钮显示 根据自己需求设置</span><br><span class="line">                NSLog(@&quot;time 倒计时结束&quot;);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;else&#123;</span><br><span class="line">            int minutes &#x3D; timeout &#x2F; 60;</span><br><span class="line">            int seconds &#x3D; timeout % 60;</span><br><span class="line">            NSString *strTime &#x3D; [NSString stringWithFormat:@&quot;%d分%.2d秒后重新获取验证码&quot;,minutes, seconds];</span><br><span class="line">            dispatch_async(dispatch_get_main_queue(), ^&#123;</span><br><span class="line">                &#x2F;&#x2F;设置界面的按钮显示 根据自己需求设置</span><br><span class="line">                NSLog(@&quot;strTime %@&quot;,strTime);</span><br><span class="line">            &#125;);</span><br><span class="line">            timeout--;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    &#x2F;&#x2F;启动timer</span><br><span class="line">    dispatch_resume(_timer);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="线程安全"><a href="#线程安全" class="headerlink" title="线程安全"></a>线程安全</h3><p>只有当多个线程同时去访问并且修改一块资源的内容时可能会有线程安全问题。</p>
<p>当多线程只是访问并不做修改，不会出现线程安全问题。</p>
<p>举例：</p>
<p>假设银行卡有余额1000元，有2个线程A、B；<br>现在A、B同时查询余额，得到的都是1000元；<br>然后A线程存入1000元，余额为1000 + 1000 = 2000；<br>然后B线程取出500元，余额为1000 - 500 = 500；<br>这样就出现线程安全问题了，下次去查询余额就只有500了😹</p>
<p>为了保证线程安全，需要给线程加锁。</p>
<h5 id="同步方案"><a href="#同步方案" class="headerlink" title="同步方案"></a>同步方案</h5><h6 id="OSSpinLock"><a href="#OSSpinLock" class="headerlink" title="OSSpinLock"></a>OSSpinLock</h6><p>OSSpinLock叫做”自旋锁”，等待锁的线程会处于忙等（busy-wait）状态，一直占用着CPU资源</p>
<p>目前已经不再安全，可能会出现优先级反转问题</p>
<p>如果等待锁的线程优先级较高，它会一直占用着CPU资源，优先级低的线程就无法释放锁</p>
<p>需要导入头文件#import &lt;libkern/OSAtomic.h&gt;</p>
<h6 id="os-unfair-lock"><a href="#os-unfair-lock" class="headerlink" title="os_unfair_lock"></a>os_unfair_lock</h6><p>os_unfair_lock用于取代不安全的OSSpinLock ，从iOS10开始才支持</p>
<p>从底层调用看，等待os_unfair_lock锁的线程会处于休眠状态，并非忙等</p>
<p>需要导入头文件#import &lt;os/lock.h&gt;</p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2361" alt="6a32356cd62bffb395c55eff0729375d.png"> </p>
<h6 id="pthread-mutex"><a href="#pthread-mutex" class="headerlink" title="pthread_mutex"></a>pthread_mutex</h6><p>mutex叫做”互斥锁”，等待锁的线程会处于休眠状态</p>
<p>需要导入头文件#import &lt;pthread.h&gt;</p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2362" alt="9848ab391ea130b47de60ac90949c5d7.png"><br><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2363" alt="1e9bf9133cafc7fe1e68e53239f70754.png"><br><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2364" alt="8d423ed41c44ae90fe1f4e55b26964f9.png"><br><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2365" alt="eeeb5c6a5d872001eb1f1c7917d810e0.png"></p>
<h6 id="dispatch-semaphore"><a href="#dispatch-semaphore" class="headerlink" title="dispatch_semaphore"></a>dispatch_semaphore</h6><p>semaphore叫做”信号量”</p>
<p>信号量的初始值，可以用来控制线程并发访问的最大数量</p>
<p>信号量的初始值为1，代表同时只允许1条线程访问资源，保证线程同步</p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2366" alt="844e0aa7a21c56f9568325ebf1df04b2.png"></p>
<h6 id="dispatch-queue-DISPATCH-QUEUE-SERIAL"><a href="#dispatch-queue-DISPATCH-QUEUE-SERIAL" class="headerlink" title="dispatch_queue(DISPATCH_QUEUE_SERIAL)"></a>dispatch_queue(DISPATCH_QUEUE_SERIAL)</h6><p>直接使用GCD的串行队列，也是可以实现线程同步的</p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2367" alt="4da7c092e947d453289322166866701b.png"></p>
<h6 id="NSLock、NSRecursiveLock"><a href="#NSLock、NSRecursiveLock" class="headerlink" title="NSLock、NSRecursiveLock"></a>NSLock、NSRecursiveLock</h6><p>NSLock是对mutex普通锁的封装</p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2371" alt="1cb32ab8ac4967086fbbee75dbb15433.png"><br><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2372" alt="f288c06d4f718baddb110b329023936d.png"><br><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2373" alt="c02fbd60aa09ed858fec23c3b8d4a6a3.png"></p>
<p>NSRecursiveLock也是对mutex递归锁的封装，API跟NSLock基本一致</p>
<h6 id="NSCondition"><a href="#NSCondition" class="headerlink" title="NSCondition"></a>NSCondition</h6><p>NSCondition是对mutex和cond的封装</p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2370" alt="5f93ec4282fecd19e347dd383be722ec.png"></p>
<h6 id="NSConditionLock"><a href="#NSConditionLock" class="headerlink" title="NSConditionLock"></a>NSConditionLock</h6><p>NSConditionLock是对NSCondition的进一步封装，可以设置具体的条件值</p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2369" alt="65483fd9373eed4ce11ef7ecb2b60fda.png"></p>
<h6 id="synchronized"><a href="#synchronized" class="headerlink" title="@synchronized"></a>@synchronized</h6><p>@synchronized是对mutex递归锁的封装</p>
<p>源码查看：objc4中的objc-sync.mm文件</p>
<p>@synchronized(obj)内部会生成obj对应的递归锁，然后进行加锁、解锁操作</p>
<p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2368" alt="27d559c23065755f60c6afe842dd3096.png"></p>
<h5 id="同步方案性能比较"><a href="#同步方案性能比较" class="headerlink" title="同步方案性能比较"></a>同步方案性能比较</h5><p>性能从高到低排序<br>os_unfair_lock<br>OSSpinLock<br>dispatch_semaphore<br>pthread_mutex<br>dispatch_queue(DISPATCH_QUEUE_SERIAL)<br>NSLock<br>NSCondition<br>pthread_mutex(recursive)<br>NSRecursiveLock<br>NSConditionLock<br>@synchronized</p>
<h5 id="自旋-互斥锁"><a href="#自旋-互斥锁" class="headerlink" title="自旋 / 互斥锁"></a>自旋 / 互斥锁</h5><h6 id="什么是自旋锁"><a href="#什么是自旋锁" class="headerlink" title="什么是自旋锁"></a>什么是自旋锁</h6><p>等待锁的线程会处于忙等（busy-wait）状态，一直占用着CPU资源</p>
<h6 id="什么是互斥锁"><a href="#什么是互斥锁" class="headerlink" title="什么是互斥锁"></a>什么是互斥锁</h6><p>等待锁的线程会处于休眠状态</p>
<h6 id="什么情况使用自旋锁更划算？"><a href="#什么情况使用自旋锁更划算？" class="headerlink" title="什么情况使用自旋锁更划算？"></a>什么情况使用自旋锁更划算？</h6><p>预计线程等待锁的时间很短<br>加锁的代码（临界区）经常被调用，但竞争情况很少发生<br>CPU资源不紧张<br>多核处理器</p>
<h6 id="什么情况使用互斥锁更划算？"><a href="#什么情况使用互斥锁更划算？" class="headerlink" title="什么情况使用互斥锁更划算？"></a>什么情况使用互斥锁更划算？</h6><p>预计线程等待锁的时间较长<br>单核处理器<br>临界区有IO操作<br>临界区代码复杂或者循环量大<br>临界区竞争非常激烈 </p>
<h5 id="高性能读写安全方案"><a href="#高性能读写安全方案" class="headerlink" title="高性能读写安全方案"></a>高性能读写安全方案</h5><h6 id="实现思路"><a href="#实现思路" class="headerlink" title="实现思路"></a>实现思路</h6><p>同一时间，只能有1个线程进行写的操作<br>同一时间，允许有多个线程进行读的操作<br>同一时间，不允许既有写的操作，又有读的操作</p>
<h6 id="实现方案"><a href="#实现方案" class="headerlink" title="实现方案"></a>实现方案</h6><p>“多读单写”，经常用于文件等数据的读写操作，实现方案有<br>pthread_rwlock：读写锁<br>dispatch_barrier_async：异步栅栏调用</p>
<p>demo:</p>
<p>由于 NSMutableDictionary 线程是不安全的。</p>
<p>现在实现一个线程安全的可变字典</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line">typedef void (^ThreadSafeBlock)(ThreadSafeMutableDictionary *dict, NSString *key, id object);</span><br><span class="line"></span><br><span class="line">@implementation ThreadSafeMutableDictionary &#123;</span><br><span class="line">    dispatch_queue_t _concurrentQueue;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (instancetype)init &#123;</span><br><span class="line">    if (self &#x3D; [super init]) &#123;</span><br><span class="line">        _concurrentQueue &#x3D; dispatch_queue_create(@&quot;com.thread.ThreadSafeMutableDictionary&quot;, DISPATCH_QUEUE_CONCURRENT);</span><br><span class="line">    &#125;</span><br><span class="line">    return self;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (void)objectForKey:(NSString *)key block:(ThreadSafeBlock)block &#123;</span><br><span class="line">    id cKey &#x3D; [key copy];</span><br><span class="line">    __weak __typeof__(self) weakSelf &#x3D; self;</span><br><span class="line"></span><br><span class="line">    dispatch_sync(_concurrentQueue, ^&#123;</span><br><span class="line">        ThreadSafeMutableDictionary *strongSelf &#x3D; weakSelf;</span><br><span class="line">        if (!strongSelf) &#123;</span><br><span class="line">            block(nil,cKey,nil);</span><br><span class="line">            return ;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        id object &#x3D; [strongSelf objectForKey:cKey];</span><br><span class="line">        block(strongSelf,cKey,object);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (void)setObject:(id)object forKey:(NSString *)key block:(ThreadSafeBlock)block &#123;</span><br><span class="line">    if (!key || !object) &#123;</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    NSString *aKey &#x3D; [key copy];</span><br><span class="line">    __weak __typeof__(self) weakSelf &#x3D; self;</span><br><span class="line">    dispatch_barrier_async(_concurrentQueue, ^&#123;</span><br><span class="line">        ThreadSafeMutableDictionary *strongSelf &#x3D; weakSelf;</span><br><span class="line">        if (!strongSelf) &#123;</span><br><span class="line">            block(nil,aKey,nil);</span><br><span class="line">            return ;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        [self setObject:object forKey:aKey];</span><br><span class="line">        if (block) &#123;</span><br><span class="line">            block(strongSelf,aKey,object);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="多线程优化"><a href="#多线程优化" class="headerlink" title="多线程优化"></a>多线程优化</h3><h5 id="优化思路"><a href="#优化思路" class="headerlink" title="优化思路"></a>优化思路</h5><h6 id="尽量减少队列切换"><a href="#尽量减少队列切换" class="headerlink" title="尽量减少队列切换"></a>尽量减少队列切换</h6><p>当线程数量超过 CPU 核心数量，CPU 核心通过线程调度切换用户态线程，意味着有上下文的转换，过多的上下文切换会带来资源开销。 </p>
<p>如下代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">dispatch_queue_t queue &#x3D; dispatch_queue_create(&quot;x.x.x&quot;, DISPATCH_QUEUE_CONCURRENT);</span><br><span class="line">- (void)tast1 &#123;</span><br><span class="line">    dispatch_async(queue, ^&#123;</span><br><span class="line">        &#x2F;&#x2F;执行任务1</span><br><span class="line">        dispatch_async(dispatch_get_main_queue(), ^&#123;</span><br><span class="line">            &#x2F;&#x2F;任务1完成</span><br><span class="line">            [self tast2];</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br><span class="line">- (void)tast2 &#123;</span><br><span class="line">    dispatch_async(queue, ^&#123;</span><br><span class="line">        &#x2F;&#x2F;执行任务2</span><br><span class="line">        dispatch_async(dispatch_get_main_queue(), ^&#123;</span><br><span class="line">            &#x2F;&#x2F;任务2完成</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这里创建了一个并行队列，调用 tast1 会执行两个任务，任务2要等待任务1执行完成，这里一共有四次队列的切换。其实是没必要的。</p>
<p>优化后：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">dispatch_queue_t queue &#x3D; dispatch_queue_create(&quot;x.x.x&quot;, DISPATCH_QUEUE_SERIAL);</span><br><span class="line">dispatch_async(queue, ^&#123;</span><br><span class="line">    &#x2F;&#x2F;执行任务1</span><br><span class="line">    &#x2F;&#x2F;执行任务2</span><br><span class="line">    dispatch_async(dispatch_get_main_queue(), ^&#123;</span><br><span class="line">        &#x2F;&#x2F;任务1、2完成</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h6 id="控制线程数量"><a href="#控制线程数量" class="headerlink" title="控制线程数量"></a>控制线程数量</h6><p>使用 GCD 并行队列，当任务过多且耗时较长时，队列会开辟大量的线程，而部分线程里面的耗时任务已经耗尽了 CPU 资源，所以其他的线程也只能等待 CPU 时间片，过多的线程也会让线程调度过于频繁。</p>
<p>GCD 中并行队列并不能限制线程数量，可以创建多个串行队列来模拟并行的效果，业界知名框架 YYKit 就做了这个逻辑，通过和 CPU 核心数量相同的串行队列轮询返回来达到并行队列的效果</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">static dispatch_queue_t YYAsyncLayerGetDisplayQueue() &#123;</span><br><span class="line">&#x2F;&#x2F;最大队列数量</span><br><span class="line">#define MAX_QUEUE_COUNT 16</span><br><span class="line">&#x2F;&#x2F;队列数量</span><br><span class="line">    static int queueCount;</span><br><span class="line">&#x2F;&#x2F;使用栈区的数组存储队列</span><br><span class="line">    static dispatch_queue_t queues[MAX_QUEUE_COUNT];</span><br><span class="line">    static dispatch_once_t onceToken;</span><br><span class="line">    static int32_t counter &#x3D; 0;</span><br><span class="line">    dispatch_once(&amp;onceToken, ^&#123;</span><br><span class="line">&#x2F;&#x2F;串行队列数量和处理器数量相同</span><br><span class="line">        queueCount &#x3D; (int)[NSProcessInfo processInfo].activeProcessorCount;</span><br><span class="line">        queueCount &#x3D; queueCount &lt; 1 ? 1 : queueCount &gt; MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : queueCount;</span><br><span class="line">&#x2F;&#x2F;创建串行队列，设置优先级</span><br><span class="line">        if ([UIDevice currentDevice].systemVersion.floatValue &gt;&#x3D; 8.0) &#123;</span><br><span class="line">            for (NSUInteger i &#x3D; 0; i &lt; queueCount; i++) &#123;</span><br><span class="line">                dispatch_queue_attr_t attr &#x3D; dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0);</span><br><span class="line">                queues[i] &#x3D; dispatch_queue_create(&quot;com.ibireme.yykit.render&quot;, attr);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            for (NSUInteger i &#x3D; 0; i &lt; queueCount; i++) &#123;</span><br><span class="line">                queues[i] &#x3D; dispatch_queue_create(&quot;com.ibireme.yykit.render&quot;, DISPATCH_QUEUE_SERIAL);</span><br><span class="line">                dispatch_set_target_queue(queues[i], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">&#x2F;&#x2F;轮询返回队列</span><br><span class="line">    uint32_t cur &#x3D; (uint32_t)OSAtomicIncrement32(&amp;counter);</span><br><span class="line">    return queues[cur % queueCount];</span><br><span class="line">#undef MAX_QUEUE_COUNT</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h6 id="线程优先级权衡"><a href="#线程优先级权衡" class="headerlink" title="线程优先级权衡"></a>线程优先级权衡</h6><p>线程调度除了轮转法以外，还有优先级调度的方案，在线程调度时，高优先级的线程会更早的执行。有两个概念需要明确：<br>    a. IO 密集型线程：频繁等待的线程，等待的时候会让出时间片。<br>    b. CPU 密集型线程：很少等待的线程，意味着长时间占用着 CPU。 </p>
<p>这样就会存在一个问题：<br>    当CPU密集型线程优先级较高，而且长期霸占CPU大部分资源，这样IO密集型线程由于优先级较低，就持续的等待，产生线程饿死的现象。这时系统会根据情况提高IO密集型线程的优先级，但即使这样，等待也是需要时间的。</p>
<p>这样可以考虑优化的方向：<br>    a. 让 IO 密集型线程优先级高于 CPU 密集型线程。<br>    b. 让紧急的任务拥有更高的优先级。</p>
<h6 id="主线程任务的优化"><a href="#主线程任务的优化" class="headerlink" title="主线程任务的优化"></a>主线程任务的优化</h6><p>这摘抄了yykit作者的提出的部分内容<a target="_blank" rel="noopener" href="https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/">iOS 保持界面流畅的技巧</a></p>
<p>一些耗时的操作尽量移动到子线程执行。</p>
<p>但是有些操作必须在主线程，比如 UI 类组件的初始化及其布局。 </p>
<p>那么主线程的优化有哪些方案可以参考呢？</p>
<p>1、尽量使用轻量级对象</p>
<p>对象的创建会分配内存、调整属性、甚至还有读取文件等操作，比较消耗 CPU 资源。比如 CALayer 比 UIView 要轻量许多，那么不需要响应触摸事件的控件，用 CALayer 显示会更加合适。</p>
<p>2、尽量减少对象的调整</p>
<p>当视图层次调整时，UIView、CALayer 之间会出现很多方法调用与通知，所以在优化性能时，应该尽量避免调整视图层次、添加和移除视图。</p>
<p>3、对象销毁其实是可以在后台线程执行的</p>
<p>这时yykit作者提倡的方式：</p>
<p>需要注意的是下面的例子中，如果self.array在别的地方还有依赖，那tmp不是唯一的销毁helper。这一套就没用了。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">NSArray *tmp &#x3D; self.array;</span><br><span class="line">self.array &#x3D; nil;</span><br><span class="line">dispatch_async(queue, ^&#123;</span><br><span class="line">    [tmp class];</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>4、内存复用</p>
<p>最常见的就是cell复用，避免了大量cell对象的创建，节省内存的同时也节省了开辟内存所消耗的时间。</p>
<p>5、懒加载任务</p>
<p>懒加载对象，用到时再创建，可以减少没必要的内存开销。开辟内存是要时间的。勉强算是变相的吧任务进行了拆分，不必初始化时就创建一系列对象。</p>
<p>6、任务拆分排队执行</p>
<p>将大量的任务拆分开来，监听Runloop运行状态，当runloop将要休息的时候，让 Runloop 循环周期执行少量任务。</p>
<p>。。。</p>
<h3 id="学习"><a href="#学习" class="headerlink" title="学习"></a>学习</h3><p><a href="https://github.com/apple/swift-corelibs-libdispatch">GCD源码</a></p>
<p><a target="_blank" rel="noopener" href="http://www.gnustep.org/resources/downloads.php">GNUstep</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.ibireme.com/2016/01/16/spinlock_is_unsafe_in_ios/">不再安全的 OSSpinLock</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/">iOS 保持界面流畅的技巧</a></p>
<p><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/594d15d6c6a7">iOS 如何高效的使用多线程</a></p>
<p><a target="_blank" rel="noopener" href="https://xiaozhuanlan.com/u/3785694919">深入浅出 GCD</a></p>
<h3 id="Demo"><a href="#Demo" class="headerlink" title="Demo"></a>Demo</h3><p><a href="https://github.com/Baichenghui/Study/tree/master/threadDemo">threadDemo</a></p>

    

    

    

  </div>
  <div class="article-info article-info-index">
    
    
    

    
    <p class="article-more-link">
      <a class="article-more-a" href="/2019/10/20/GCD%E7%A0%94%E7%A9%B6/">展开全文 >></a>
    </p>
    

    
    <div class="clearfix"></div>
  </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-YBTaskScheduler-研究" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
  
  
    <h1 itemprop="name">
	
	  <a class="article-title" href="/2019/10/20/YBTaskScheduler-%E7%A0%94%E7%A9%B6/" target="_blank">YBTaskScheduler 研究</a>
  
    </h1>
  


  
  
<a href="/2019/10/20/YBTaskScheduler-%E7%A0%94%E7%A9%B6/" class="archive-article-date">
        <time datetime="2019-10-20T02:52:15.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2019-10-20</time>
</a>

  
  
    

  
  </header>
  
  <div class="article-entry" itemprop="articleBody">
    
    <p>学习<br><a href="https://github.com/indulgeIn/YBTaskScheduler">YBTaskScheduler</a></p>
<p><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/f2a610c77d26">iOS 任务调度器：为 CPU 和内存减负</a></p>
<p>[TOC]</p>
<h3 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h3><h5 id="可能遇到的问题"><a href="#可能遇到的问题" class="headerlink" title="可能遇到的问题"></a>可能遇到的问题</h5><p>当主线程执行大量的任务会造成卡顿，应该把这些任务移动到子线程异步执行。</p>
<p>但是异步执行的任务量过大，会导致cpu和内存占用率过高，然后引发一系列其他问题。</p>
<h5 id="解决思路"><a href="#解决思路" class="headerlink" title="解决思路"></a>解决思路</h5><h6 id="延时降频"><a href="#延时降频" class="headerlink" title="延时降频"></a>延时降频</h6><p>降低任务执行频率，或延缓任务执行时机，以时间换空间。</p>
<h6 id="淘汰"><a href="#淘汰" class="headerlink" title="淘汰"></a>淘汰</h6><p>淘汰不必要执行的任务，如已经滑出屏幕的ui异步绘制任务就可以移除，只保留需要显示屏幕可见部分的绘制任务。</p>
<h6 id="优先级调度"><a href="#优先级调度" class="headerlink" title="优先级调度"></a>优先级调度</h6><p>个人理解这个对降低cpu、内存占用率高没什么作用，允许设置优先级应该是一个附加功能，真正起到作用的是使用runloop起到降频（任务到runloop空闲时才执行）的作用。</p>
<h3 id="原理和实现"><a href="#原理和实现" class="headerlink" title="原理和实现"></a>原理和实现</h3><h5 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h5><p><code>YBTaskScheduler</code> 监听runloop的状态，只有在runloop将要进入空闲状态时才执行任务。</p>
<p>设置任务最大数量，在添加任务的时候将超过最大限制的任务淘汰。</p>
<h5 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h5><h6 id="命令模式-runloop"><a href="#命令模式-runloop" class="headerlink" title="命令模式 + runloop"></a>命令模式 + runloop</h6><p>想要管理这些复杂的任务，并且在合适的时机调用它们，自然而然的就想到了命令模式。意味着任务不能直接执行，而是把任务作为一个命令装入容器。</p>
<p>在 Objective-C 中，显然 Block 代码块能解决延迟执行这个问题：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[_scheduler addTask:^&#123;</span><br><span class="line">     &#x2F;* </span><br><span class="line">     具体任务代码</span><br><span class="line">     解压图片、裁剪图片、访问磁盘等 </span><br><span class="line">     *&#x2F;</span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure>

<p>然后组件将这些代码块“装起来”，组件由此“掌握”了所有的任务，可以自由的决定何时调用这些代码块，何时对某些代码块进行淘汰，还可以实现优先级调度。 </p>
<p>既然是命令模式，还差一个 Invoker (调用程序)，即何时去触发这些任务。结合 iOS 的技术特点，可以监听 RunLoop 循环周期来实现：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">static void addRunLoopObserver() &#123;</span><br><span class="line">    static dispatch_once_t onceToken;</span><br><span class="line">    dispatch_once(&amp;onceToken, ^&#123;</span><br><span class="line">        taskSchedulers &#x3D; [NSHashTable weakObjectsHashTable];</span><br><span class="line">        CFRunLoopObserverRef observer &#x3D; CFRunLoopObserverCreate(CFAllocatorGetDefault(), kCFRunLoopBeforeWaiting | kCFRunLoopExit, true, 0xFFFFFF, runLoopObserverCallBack, NULL);</span><br><span class="line">        CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);</span><br><span class="line">        CFRelease(observer);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后在回调函数中进行任务的调度。</p>
<h6 id="策略模式"><a href="#策略模式" class="headerlink" title="策略模式"></a>策略模式</h6><p><img src="evernotecid://3252F98A-C8F0-41C3-834D-D7F7BDDE3269/appyinxiangcom/11846024/ENResource/p2355" alt="d4d2cc7e6fc6db790b737872f1549666.png"></p>
<p>具体策略：</p>
<p>栈：后加入的任务先执行（可以理解为后加入的任务优先级高），优先淘汰先加入的任务。</p>
<p>队列：先加入的任务先执行（可以理解为先加入的任务优先级高），优先淘汰后加入的任务。</p>
<p>优先队列：自定义任务优先级，不支持任务淘汰。</p>
<h6 id="数据结构"><a href="#数据结构" class="headerlink" title="数据结构"></a>数据结构</h6><p>为了实现淘汰策略和优先级调度，作者直接使用了 C++ 的数据结构：deque 和priority_queue。</p>
<p>要实现任务淘汰，所以使用 deque 双端队列来模拟栈和队列，而不是直接使用 stack 和 queue。</p>
<p>使用 priority_queue 优先队列来处理自定义的优先级调度，它的缺点是不能删除低优先级节点，为了节约时间成本姑且够用。</p>
<h6 id="线程安全"><a href="#线程安全" class="headerlink" title="线程安全"></a>线程安全</h6><p>任务的调度可能在任意线程，所以必须要做好容器（栈、队列、优先队列）访问的线程安全问题，组件是使用 pthread_mutex_t 和 dispatch_once 来保证线程安全，同时笔者尽量减少临界区来提高性能。</p>
<p>值得注意的是，如果不会存在线程安全的代码就不要去加锁了。</p>
<h3 id="亮点"><a href="#亮点" class="headerlink" title="亮点"></a>亮点</h3><h5 id="设计模式"><a href="#设计模式" class="headerlink" title="设计模式"></a>设计模式</h5><p>使用策略模式区分不同淘汰策略，以及任务优先级设置。</p>
<p>使用命令模式，将任务添加和任务调度隔离。可以自由的决定何时调用这些代码块，何时对某些代码块进行淘汰，还可以实现优先级调度。</p>
<h5 id="Runloop"><a href="#Runloop" class="headerlink" title="Runloop"></a>Runloop</h5><p>监听runloop回调状态</p>
<p>将任务放在runloop空闲状态才下执行，避免任务集中执行，起到延缓作用。</p>
<h5 id="数据结构-1"><a href="#数据结构-1" class="headerlink" title="数据结构"></a>数据结构</h5><p>使用了 C++ 的数据结构：deque 和priority_queue。</p>
<p>使用 deque 双端队列来模拟栈和队列。</p>
<p>使用 priority_queue 优先队列来处理自定义的优先级调度。</p>

    

    

    

  </div>
  <div class="article-info article-info-index">
    
    
    

    
    <p class="article-more-link">
      <a class="article-more-a" href="/2019/10/20/YBTaskScheduler-%E7%A0%94%E7%A9%B6/">展开全文 >></a>
    </p>
    

    
    <div class="clearfix"></div>
  </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
  
    <nav id="page-nav">
      <span class="page-number current">1</span><a class="page-number" href="/page/2/">2</a><a class="extend next" rel="next" href="/page/2/">Next &amp;raquo;</a>
    </nav>
  


          </div>
        </div>
      </div>
      <footer id="footer">
  <div class="outer">
    <div id="footer-info">
    	<div class="footer-left">
    		&copy; 2018-2020 <a href="https://github.com/Baichenghui/" target="_blank">b9i60000</a>
    	</div>
      	<div class="footer-right">
			
			
      		GitHub:<a href="https://github.com/JoeyBling/hexo-theme-yilia-plus" target="_blank">hexo-theme-yilia-plus</a> by Litten
      	</div>
    </div>
  </div>
  
  
	<script src="/lib/busuanzi.pure.js"></script>
	
  
  
	
	<span id="busuanzi_container_site_pv" style="display:none">
		本站总访问量<span id="busuanzi_value_site_pv"></span>次	
	        <span class="post-meta-divider" >|</span>
	</span>
  	<span id="busuanzi_container_site_uv" style='display:none'>
  		本站访客数<span id="busuanzi_value_site_uv"></span>人
  	</span>
  
</footer>

    </div>
    <script>
	var yiliaConfig = {
		mathjax: false,
		isHome: true,
		isPost: false,
		isArchive: false,
		isTag: false,
		isCategory: false,
		open_in_new: true,
		toc_hide_index: true,
		root: "/",
		innerArchive: true,
		showTags: true
	}
</script>

<script>!function(r){function e(t){if(i[t])return i[t].exports;var n=i[t]={exports:{},id:t,loaded:!1};return r[t].call(n.exports,n,n.exports,e),n.loaded=!0,n.exports}var i={};e.m=r,e.c=i,e.p="./",e(0)}([function(t,n,r){r(208),t.exports=r(205)},function(t,n,r){var d=r(3),y=r(46),g=r(26),b=r(27),x=r(47),m="prototype",S=function(t,n,r){var e,i,o,u,c=t&S.F,f=t&S.G,a=t&S.S,s=t&S.P,l=t&S.B,h=f?d:a?d[n]||(d[n]={}):(d[n]||{})[m],v=f?y:y[n]||(y[n]={}),p=v[m]||(v[m]={});for(e in f&&(r=n),r)o=((i=!c&&h&&void 0!==h[e])?h:r)[e],u=l&&i?x(o,d):s&&"function"==typeof o?x(Function.call,o):o,h&&b(h,e,o,t&S.U),v[e]!=o&&g(v,e,u),s&&p[e]!=o&&(p[e]=o)};d.core=y,S.F=1,S.G=2,S.S=4,S.P=8,S.B=16,S.W=32,S.U=64,S.R=128,t.exports=S},function(t,n,r){var e=r(5);t.exports=function(t){if(!e(t))throw TypeError(t+" is not an object!");return t}},function(t,n){var r=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=r)},function(t,n){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,n){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,n){var r=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=r)},function(t,n,r){var e=r(118)("wks"),i=r(79),o=r(3).Symbol,u="function"==typeof o;(t.exports=function(t){return e[t]||(e[t]=u&&o[t]||(u?o:i)("Symbol."+t))}).store=e},function(t,n,r){var e=r(49),i=Math.min;t.exports=function(t){return 0<t?i(e(t),9007199254740991):0}},function(t,n){var r={}.hasOwnProperty;t.exports=function(t,n){return r.call(t,n)}},function(t,n,r){t.exports=!r(4)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,n,r){var e=r(2),i=r(174),o=r(53),u=Object.defineProperty;n.f=r(10)?Object.defineProperty:function(t,n,r){if(e(t),n=o(n,!0),e(r),i)try{return u(t,n,r)}catch(t){}if("get"in r||"set"in r)throw TypeError("Accessors not supported!");return"value"in r&&(t[n]=r.value),t}},function(t,n,r){t.exports=!r(20)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,n,r){var e=r(14),i=r(24);t.exports=r(12)?function(t,n,r){return e.f(t,n,i(1,r))}:function(t,n,r){return t[n]=r,t}},function(t,n,r){var e=r(22),i=r(60),o=r(42),u=Object.defineProperty;n.f=r(12)?Object.defineProperty:function(t,n,r){if(e(t),n=o(n,!0),e(r),i)try{return u(t,n,r)}catch(t){}if("get"in r||"set"in r)throw TypeError("Accessors not supported!");return"value"in r&&(t[n]=r.value),t}},function(t,n,r){var e=r(96),i=r(34);t.exports=function(t){return e(i(t))}},function(t,n,r){var e=r(40)("wks"),i=r(25),o=r(6).Symbol,u="function"==typeof o;(t.exports=function(t){return e[t]||(e[t]=u&&o[t]||(u?o:i)("Symbol."+t))}).store=e},function(t,n,r){var e=r(51);t.exports=function(t){return Object(e(t))}},function(t,n){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,n){var r=t.exports={version:"2.6.9"};"number"==typeof __e&&(__e=r)},function(t,n){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,n,r){var e=r(18);t.exports=function(t){if(!e(t))throw TypeError(t+" is not an object!");return t}},function(t,n){t.exports=!0},function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},function(t,n){var r=0,e=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++r+e).toString(36))}},function(t,n,r){var e=r(11),i=r(75);t.exports=r(10)?function(t,n,r){return e.f(t,n,i(1,r))}:function(t,n,r){return t[n]=r,t}},function(t,n,r){var o=r(3),u=r(26),c=r(30),f=r(79)("src"),e=r(219),i="toString",a=(""+e).split(i);r(46).inspectSource=function(t){return e.call(t)},(t.exports=function(t,n,r,e){var i="function"==typeof r;i&&(c(r,"name")||u(r,"name",n)),t[n]!==r&&(i&&(c(r,f)||u(r,f,t[n]?""+t[n]:a.join(String(n)))),t===o?t[n]=r:e?t[n]?t[n]=r:u(t,n,r):(delete t[n],u(t,n,r)))})(Function.prototype,i,function(){return"function"==typeof this&&this[f]||e.call(this)})},function(t,n,r){var e=r(1),i=r(4),u=r(51),c=/"/g,o=function(t,n,r,e){var i=String(u(t)),o="<"+n;return""!==r&&(o+=" "+r+'="'+String(e).replace(c,"&quot;")+'"'),o+">"+i+"</"+n+">"};t.exports=function(n,t){var r={};r[n]=t(o),e(e.P+e.F*i(function(){var t=""[n]('"');return t!==t.toLowerCase()||3<t.split('"').length}),"String",r)}},function(t,n,r){var e=r(65),i=r(35);t.exports=Object.keys||function(t){return e(t,i)}},function(t,n){var r={}.hasOwnProperty;t.exports=function(t,n){return r.call(t,n)}},function(t,n,r){var e=r(117),i=r(75),o=r(33),u=r(53),c=r(30),f=r(174),a=Object.getOwnPropertyDescriptor;n.f=r(10)?a:function(t,n){if(t=o(t),n=u(n,!0),f)try{return a(t,n)}catch(t){}if(c(t,n))return i(!e.f.call(t,n),t[n])}},function(t,n,r){var e=r(30),i=r(17),o=r(154)("IE_PROTO"),u=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=i(t),e(t,o)?t[o]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?u:null}},function(t,n,r){var e=r(116),i=r(51);t.exports=function(t){return e(i(t))}},function(t,n){t.exports=function(t){if(null==t)throw TypeError("Can't call method on  "+t);return t}},function(t,n){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,n){t.exports={}},function(t,n){n.f={}.propertyIsEnumerable},function(t,n,r){var e=r(14).f,i=r(9),o=r(16)("toStringTag");t.exports=function(t,n,r){t&&!i(t=r?t:t.prototype,o)&&e(t,o,{configurable:!0,value:n})}},function(t,n,r){var e=r(40)("keys"),i=r(25);t.exports=function(t){return e[t]||(e[t]=i(t))}},function(t,n,r){var e=r(19),i=r(6),o="__core-js_shared__",u=i[o]||(i[o]={});(t.exports=function(t,n){return u[t]||(u[t]=void 0!==n?n:{})})("versions",[]).push({version:e.version,mode:r(23)?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},function(t,n){var r=Math.ceil,e=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(0<t?e:r)(t)}},function(t,n,r){var i=r(18);t.exports=function(t,n){if(!i(t))return t;var r,e;if(n&&"function"==typeof(r=t.toString)&&!i(e=r.call(t)))return e;if("function"==typeof(r=t.valueOf)&&!i(e=r.call(t)))return e;if(!n&&"function"==typeof(r=t.toString)&&!i(e=r.call(t)))return e;throw TypeError("Can't convert object to primitive value")}},function(t,n,r){var e=r(6),i=r(19),o=r(23),u=r(44),c=r(14).f;t.exports=function(t){var n=i.Symbol||(i.Symbol=o?{}:e.Symbol||{});"_"==t.charAt(0)||t in n||c(n,t,{value:u.f(t)})}},function(t,n,r){n.f=r(16)},function(t,n){var r={}.toString;t.exports=function(t){return r.call(t).slice(8,-1)}},function(t,n){var r=t.exports={version:"2.6.9"};"number"==typeof __e&&(__e=r)},function(t,n,r){var o=r(21);t.exports=function(e,i,t){if(o(e),void 0===i)return e;switch(t){case 1:return function(t){return e.call(i,t)};case 2:return function(t,n){return e.call(i,t,n)};case 3:return function(t,n,r){return e.call(i,t,n,r)}}return function(){return e.apply(i,arguments)}}},function(t,n,r){"use strict";var e=r(4);t.exports=function(t,n){return!!t&&e(function(){n?t.call(null,function(){},1):t.call(null)})}},function(t,n){var r=Math.ceil,e=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(0<t?e:r)(t)}},function(t,n,r){var x=r(47),m=r(116),S=r(17),w=r(8),e=r(138);t.exports=function(l,t){var h=1==l,v=2==l,p=3==l,d=4==l,y=6==l,g=5==l||y,b=t||e;return function(t,n,r){for(var e,i,o=S(t),u=m(o),c=x(n,r,3),f=w(u.length),a=0,s=h?b(t,f):v?b(t,0):void 0;a<f;a++)if((g||a in u)&&(i=c(e=u[a],a,o),l))if(h)s[a]=i;else if(i)switch(l){case 3:return!0;case 5:return e;case 6:return a;case 2:s.push(e)}else if(d)return!1;return y?-1:p||d?d:s}}},function(t,n){t.exports=function(t){if(null==t)throw TypeError("Can't call method on  "+t);return t}},function(t,n,r){var i=r(1),o=r(46),u=r(4);t.exports=function(t,n){var r=(o.Object||{})[t]||Object[t],e={};e[t]=n(r),i(i.S+i.F*u(function(){r(1)}),"Object",e)}},function(t,n,r){var i=r(5);t.exports=function(t,n){if(!i(t))return t;var r,e;if(n&&"function"==typeof(r=t.toString)&&!i(e=r.call(t)))return e;if("function"==typeof(r=t.valueOf)&&!i(e=r.call(t)))return e;if(!n&&"function"==typeof(r=t.toString)&&!i(e=r.call(t)))return e;throw TypeError("Can't convert object to primitive value")}},function(t,n,r){var d=r(6),y=r(19),g=r(93),b=r(13),x=r(9),m="prototype",S=function(t,n,r){var e,i,o,u=t&S.F,c=t&S.G,f=t&S.S,a=t&S.P,s=t&S.B,l=t&S.W,h=c?y:y[n]||(y[n]={}),v=h[m],p=c?d:f?d[n]:(d[n]||{})[m];for(e in c&&(r=n),r)(i=!u&&p&&void 0!==p[e])&&x(h,e)||(o=i?p[e]:r[e],h[e]=c&&"function"!=typeof p[e]?r[e]:s&&i?g(o,d):l&&p[e]==o?function(e){var t=function(t,n,r){if(this instanceof e){switch(arguments.length){case 0:return new e;case 1:return new e(t);case 2:return new e(t,n)}return new e(t,n,r)}return e.apply(this,arguments)};return t[m]=e[m],t}(o):a&&"function"==typeof o?g(Function.call,o):o,a&&((h.virtual||(h.virtual={}))[e]=o,t&S.R&&v&&!v[e]&&b(v,e,o)))};S.F=1,S.G=2,S.S=4,S.P=8,S.B=16,S.W=32,S.U=64,S.R=128,t.exports=S},function(t,n,r){var e=r(34);t.exports=function(t){return Object(e(t))}},function(t,n,r){var o=r(196),e=r(1),i=r(118)("metadata"),u=i.store||(i.store=new(r(200))),c=function(t,n,r){var e=u.get(t);if(!e){if(!r)return;u.set(t,e=new o)}var i=e.get(n);if(!i){if(!r)return;e.set(n,i=new o)}return i};t.exports={store:u,map:c,has:function(t,n,r){var e=c(n,r,!1);return void 0!==e&&e.has(t)},get:function(t,n,r){var e=c(n,r,!1);return void 0===e?void 0:e.get(t)},set:function(t,n,r,e){c(r,e,!0).set(t,n)},keys:function(t,n){var r=c(t,n,!1),e=[];return r&&r.forEach(function(t,n){e.push(n)}),e},key:function(t){return void 0===t||"symbol"==typeof t?t:String(t)},exp:function(t){e(e.S,"Reflect",t)}}},function(t,n,r){"use strict";if(r(10)){var g=r(68),b=r(3),x=r(4),m=r(1),S=r(132),e=r(159),h=r(47),w=r(70),i=r(75),_=r(26),o=r(76),u=r(49),O=r(8),E=r(194),c=r(78),f=r(53),a=r(30),M=r(81),P=r(5),v=r(17),p=r(145),j=r(72),F=r(32),A=r(73).f,d=r(161),s=r(79),l=r(7),y=r(50),I=r(120),L=r(119),N=r(162),T=r(82),k=r(125),R=r(77),C=r(137),D=r(166),G=r(11),W=r(31),U=G.f,V=W.f,B=b.RangeError,q=b.TypeError,z=b.Uint8Array,K="ArrayBuffer",H="Shared"+K,J="BYTES_PER_ELEMENT",$="prototype",Y=Array[$],X=e.ArrayBuffer,Q=e.DataView,Z=y(0),tt=y(2),nt=y(3),rt=y(4),et=y(5),it=y(6),ot=I(!0),ut=I(!1),ct=N.values,ft=N.keys,at=N.entries,st=Y.lastIndexOf,lt=Y.reduce,ht=Y.reduceRight,vt=Y.join,pt=Y.sort,dt=Y.slice,yt=Y.toString,gt=Y.toLocaleString,bt=l("iterator"),xt=l("toStringTag"),mt=s("typed_constructor"),St=s("def_constructor"),wt=S.CONSTR,_t=S.TYPED,Ot=S.VIEW,Et="Wrong length!",Mt=y(1,function(t,n){return It(L(t,t[St]),n)}),Pt=x(function(){return 1===new z(new Uint16Array([1]).buffer)[0]}),jt=!!z&&!!z[$].set&&x(function(){new z(1).set({})}),Ft=function(t,n){var r=u(t);if(r<0||r%n)throw B("Wrong offset!");return r},At=function(t){if(P(t)&&_t in t)return t;throw q(t+" is not a typed array!")},It=function(t,n){if(!(P(t)&&mt in t))throw q("It is not a typed array constructor!");return new t(n)},Lt=function(t,n){return Nt(L(t,t[St]),n)},Nt=function(t,n){for(var r=0,e=n.length,i=It(t,e);r<e;)i[r]=n[r++];return i},Tt=function(t,n,r){U(t,n,{get:function(){return this._d[r]}})},kt=function(t){var n,r,e,i,o,u,c=v(t),f=arguments.length,a=1<f?arguments[1]:void 0,s=void 0!==a,l=d(c);if(null!=l&&!p(l)){for(u=l.call(c),e=[],n=0;!(o=u.next()).done;n++)e.push(o.value);c=e}for(s&&2<f&&(a=h(a,arguments[2],2)),n=0,r=O(c.length),i=It(this,r);n<r;n++)i[n]=s?a(c[n],n):c[n];return i},Rt=function(){for(var t=0,n=arguments.length,r=It(this,n);t<n;)r[t]=arguments[t++];return r},Ct=!!z&&x(function(){gt.call(new z(1))}),Dt=function(){return gt.apply(Ct?dt.call(At(this)):At(this),arguments)},Gt={copyWithin:function(t,n){return D.call(At(this),t,n,2<arguments.length?arguments[2]:void 0)},every:function(t){return rt(At(this),t,1<arguments.length?arguments[1]:void 0)},fill:function(t){return C.apply(At(this),arguments)},filter:function(t){return Lt(this,tt(At(this),t,1<arguments.length?arguments[1]:void 0))},find:function(t){return et(At(this),t,1<arguments.length?arguments[1]:void 0)},findIndex:function(t){return it(At(this),t,1<arguments.length?arguments[1]:void 0)},forEach:function(t){Z(At(this),t,1<arguments.length?arguments[1]:void 0)},indexOf:function(t){return ut(At(this),t,1<arguments.length?arguments[1]:void 0)},includes:function(t){return ot(At(this),t,1<arguments.length?arguments[1]:void 0)},join:function(t){return vt.apply(At(this),arguments)},lastIndexOf:function(t){return st.apply(At(this),arguments)},map:function(t){return Mt(At(this),t,1<arguments.length?arguments[1]:void 0)},reduce:function(t){return lt.apply(At(this),arguments)},reduceRight:function(t){return ht.apply(At(this),arguments)},reverse:function(){for(var t,n=this,r=At(n).length,e=Math.floor(r/2),i=0;i<e;)t=n[i],n[i++]=n[--r],n[r]=t;return n},some:function(t){return nt(At(this),t,1<arguments.length?arguments[1]:void 0)},sort:function(t){return pt.call(At(this),t)},subarray:function(t,n){var r=At(this),e=r.length,i=c(t,e);return new(L(r,r[St]))(r.buffer,r.byteOffset+i*r.BYTES_PER_ELEMENT,O((void 0===n?e:c(n,e))-i))}},Wt=function(t,n){return Lt(this,dt.call(At(this),t,n))},Ut=function(t){At(this);var n=Ft(arguments[1],1),r=this.length,e=v(t),i=O(e.length),o=0;if(r<i+n)throw B(Et);for(;o<i;)this[n+o]=e[o++]},Vt={entries:function(){return at.call(At(this))},keys:function(){return ft.call(At(this))},values:function(){return ct.call(At(this))}},Bt=function(t,n){return P(t)&&t[_t]&&"symbol"!=typeof n&&n in t&&String(+n)==String(n)},qt=function(t,n){return Bt(t,n=f(n,!0))?i(2,t[n]):V(t,n)},zt=function(t,n,r){return!(Bt(t,n=f(n,!0))&&P(r)&&a(r,"value"))||a(r,"get")||a(r,"set")||r.configurable||a(r,"writable")&&!r.writable||a(r,"enumerable")&&!r.enumerable?U(t,n,r):(t[n]=r.value,t)};wt||(W.f=qt,G.f=zt),m(m.S+m.F*!wt,"Object",{getOwnPropertyDescriptor:qt,defineProperty:zt}),x(function(){yt.call({})})&&(yt=gt=function(){return vt.call(this)});var Kt=o({},Gt);o(Kt,Vt),_(Kt,bt,Vt.values),o(Kt,{slice:Wt,set:Ut,constructor:function(){},toString:yt,toLocaleString:Dt}),Tt(Kt,"buffer","b"),Tt(Kt,"byteOffset","o"),Tt(Kt,"byteLength","l"),Tt(Kt,"length","e"),U(Kt,xt,{get:function(){return this[_t]}}),t.exports=function(t,l,n,o){var h=t+((o=!!o)?"Clamped":"")+"Array",r="get"+t,u="set"+t,v=b[h],c=v||{},e=v&&F(v),i=!v||!S.ABV,f={},a=v&&v[$],p=function(t,i){U(t,i,{get:function(){return t=i,(n=this._d).v[r](t*l+n.o,Pt);var t,n},set:function(t){return n=i,r=t,e=this._d,o&&(r=(r=Math.round(r))<0?0:255<r?255:255&r),void e.v[u](n*l+e.o,r,Pt);var n,r,e},enumerable:!0})};i?(v=n(function(t,n,r,e){w(t,v,h,"_d");var i,o,u,c,f=0,a=0;if(P(n)){if(!(n instanceof X||(c=M(n))==K||c==H))return _t in n?Nt(v,n):kt.call(v,n);i=n,a=Ft(r,l);var s=n.byteLength;if(void 0===e){if(s%l)throw B(Et);if((o=s-a)<0)throw B(Et)}else if(s<(o=O(e)*l)+a)throw B(Et);u=o/l}else u=E(n),i=new X(o=u*l);for(_(t,"_d",{b:i,o:a,l:o,e:u,v:new Q(i)});f<u;)p(t,f++)}),a=v[$]=j(Kt),_(a,"constructor",v)):x(function(){v(1)})&&x(function(){new v(-1)})&&k(function(t){new v,new v(null),new v(1.5),new v(t)},!0)||(v=n(function(t,n,r,e){var i;return w(t,v,h),P(n)?n instanceof X||(i=M(n))==K||i==H?void 0!==e?new c(n,Ft(r,l),e):void 0!==r?new c(n,Ft(r,l)):new c(n):_t in n?Nt(v,n):kt.call(v,n):new c(E(n))}),Z(e!==Function.prototype?A(c).concat(A(e)):A(c),function(t){t in v||_(v,t,c[t])}),v[$]=a,g||(a.constructor=v));var s=a[bt],d=!!s&&("values"==s.name||null==s.name),y=Vt.values;_(v,mt,!0),_(a,_t,h),_(a,Ot,!0),_(a,St,v),(o?new v(1)[xt]==h:xt in a)||U(a,xt,{get:function(){return h}}),f[h]=v,m(m.G+m.W+m.F*(v!=c),f),m(m.S,h,{BYTES_PER_ELEMENT:l}),m(m.S+m.F*x(function(){c.of.call(v,1)}),h,{from:kt,of:Rt}),J in a||_(a,J,l),m(m.P,h,Gt),R(h),m(m.P+m.F*jt,h,{set:Ut}),m(m.P+m.F*!d,h,Vt),g||a.toString==yt||(a.toString=yt),m(m.P+m.F*x(function(){new v(1).slice()}),h,{slice:Wt}),m(m.P+m.F*(x(function(){return[1,2].toLocaleString()!=new v([1,2]).toLocaleString()})||!x(function(){a.toLocaleString.call([1,2])})),h,{toLocaleString:Dt}),T[h]=d?s:y,g||d||_(a,bt,y)}}else t.exports=function(){}},function(t,n){var r={}.toString;t.exports=function(t){return r.call(t).slice(8,-1)}},function(t,n,r){var e=r(18),i=r(6).document,o=e(i)&&e(i.createElement);t.exports=function(t){return o?i.createElement(t):{}}},function(t,n,r){t.exports=!r(12)&&!r(20)(function(){return 7!=Object.defineProperty(r(59)("div"),"a",{get:function(){return 7}}).a})},function(t,n,r){"use strict";var x=r(23),m=r(54),S=r(66),w=r(13),_=r(36),O=r(98),E=r(38),M=r(104),P=r(16)("iterator"),j=!([].keys&&"next"in[].keys()),F="values",A=function(){return this};t.exports=function(t,n,r,e,i,o,u){O(r,n,e);var c,f,a,s=function(t){if(!j&&t in p)return p[t];switch(t){case"keys":case F:return function(){return new r(this,t)}}return function(){return new r(this,t)}},l=n+" Iterator",h=i==F,v=!1,p=t.prototype,d=p[P]||p["@@iterator"]||i&&p[i],y=d||s(i),g=i?h?s("entries"):y:void 0,b="Array"==n&&p.entries||d;if(b&&((a=M(b.call(new t)))!==Object.prototype&&a.next&&(E(a,l,!0),x||"function"==typeof a[P]||w(a,P,A))),h&&d&&d.name!==F&&(v=!0,y=function(){return d.call(this)}),x&&!u||!j&&!v&&p[P]||w(p,P,y),_[n]=y,_[l]=A,i)if(c={values:h?y:s(F),keys:o?y:s("keys"),entries:g},u)for(f in c)f in p||S(p,f,c[f]);else m(m.P+m.F*(j||v),n,c);return c}},function(t,n,e){var i=e(22),o=e(101),u=e(35),c=e(39)("IE_PROTO"),f=function(){},a="prototype",s=function(){var t,n=e(59)("iframe"),r=u.length;for(n.style.display="none",e(95).appendChild(n),n.src="javascript:",(t=n.contentWindow.document).open(),t.write("<script>document.F=Object<\/script>"),t.close(),s=t.F;r--;)delete s[a][u[r]];return s()};t.exports=Object.create||function(t,n){var r;return null!==t?(f[a]=i(t),r=new f,f[a]=null,r[c]=t):r=s(),void 0===n?r:o(r,n)}},function(t,n,r){var e=r(65),i=r(35).concat("length","prototype");n.f=Object.getOwnPropertyNames||function(t){return e(t,i)}},function(t,n){n.f=Object.getOwnPropertySymbols},function(t,n,r){var u=r(9),c=r(15),f=r(92)(!1),a=r(39)("IE_PROTO");t.exports=function(t,n){var r,e=c(t),i=0,o=[];for(r in e)r!=a&&u(e,r)&&o.push(r);for(;n.length>i;)u(e,r=n[i++])&&(~f(o,r)||o.push(r));return o}},function(t,n,r){t.exports=r(13)},function(t,n,r){var e=r(7)("unscopables"),i=Array.prototype;null==i[e]&&r(26)(i,e,{}),t.exports=function(t){i[e][t]=!0}},function(t,n){t.exports=!1},function(t,n,r){var e=r(79)("meta"),i=r(5),o=r(30),u=r(11).f,c=0,f=Object.isExtensible||function(){return!0},a=!r(4)(function(){return f(Object.preventExtensions({}))}),s=function(t){u(t,e,{value:{i:"O"+ ++c,w:{}}})},l=t.exports={KEY:e,NEED:!1,fastKey:function(t,n){if(!i(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!o(t,e)){if(!f(t))return"F";if(!n)return"E";s(t)}return t[e].i},getWeak:function(t,n){if(!o(t,e)){if(!f(t))return!0;if(!n)return!1;s(t)}return t[e].w},onFreeze:function(t){return a&&l.NEED&&f(t)&&!o(t,e)&&s(t),t}}},function(t,n){t.exports=function(t,n,r,e){if(!(t instanceof n)||void 0!==e&&e in t)throw TypeError(r+": incorrect invocation!");return t}},function(t,n,r){var h=r(47),v=r(177),p=r(145),d=r(2),y=r(8),g=r(161),b={},x={};(n=t.exports=function(t,n,r,e,i){var o,u,c,f,a=i?function(){return t}:g(t),s=h(r,e,n?2:1),l=0;if("function"!=typeof a)throw TypeError(t+" is not iterable!");if(p(a)){for(o=y(t.length);l<o;l++)if((f=n?s(d(u=t[l])[0],u[1]):s(t[l]))===b||f===x)return f}else for(c=a.call(t);!(u=c.next()).done;)if((f=v(c,s,u.value,n))===b||f===x)return f}).BREAK=b,n.RETURN=x},function(t,n,e){var i=e(2),o=e(183),u=e(141),c=e(154)("IE_PROTO"),f=function(){},a="prototype",s=function(){var t,n=e(140)("iframe"),r=u.length;for(n.style.display="none",e(143).appendChild(n),n.src="javascript:",(t=n.contentWindow.document).open(),t.write("<script>document.F=Object<\/script>"),t.close(),s=t.F;r--;)delete s[a][u[r]];return s()};t.exports=Object.create||function(t,n){var r;return null!==t?(f[a]=i(t),r=new f,f[a]=null,r[c]=t):r=s(),void 0===n?r:o(r,n)}},function(t,n,r){var e=r(185),i=r(141).concat("length","prototype");n.f=Object.getOwnPropertyNames||function(t){return e(t,i)}},function(t,n,r){var e=r(185),i=r(141);t.exports=Object.keys||function(t){return e(t,i)}},function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},function(t,n,r){var i=r(27);t.exports=function(t,n,r){for(var e in n)i(t,e,n[e],r);return t}},function(t,n,r){"use strict";var e=r(3),i=r(11),o=r(10),u=r(7)("species");t.exports=function(t){var n=e[t];o&&n&&!n[u]&&i.f(n,u,{configurable:!0,get:function(){return this}})}},function(t,n,r){var e=r(49),i=Math.max,o=Math.min;t.exports=function(t,n){return(t=e(t))<0?i(t+n,0):o(t,n)}},function(t,n){var r=0,e=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++r+e).toString(36))}},function(t,n,r){var e=r(5);t.exports=function(t,n){if(!e(t)||t._t!==n)throw TypeError("Incompatible receiver, "+n+" required!");return t}},function(t,n,r){var i=r(45),o=r(7)("toStringTag"),u="Arguments"==i(function(){return arguments}());t.exports=function(t){var n,r,e;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(r=function(t,n){try{return t[n]}catch(t){}}(n=Object(t),o))?r:u?i(n):"Object"==(e=i(n))&&"function"==typeof n.callee?"Arguments":e}},function(t,n){t.exports={}},function(t,n,r){var e=r(11).f,i=r(30),o=r(7)("toStringTag");t.exports=function(t,n,r){t&&!i(t=r?t:t.prototype,o)&&e(t,o,{configurable:!0,value:n})}},function(t,n,r){var u=r(1),e=r(51),c=r(4),f=r(157),i="["+f+"]",o=RegExp("^"+i+i+"*"),a=RegExp(i+i+"*$"),s=function(t,n,r){var e={},i=c(function(){return!!f[t]()||"​"!="​"[t]()}),o=e[t]=i?n(l):f[t];r&&(e[r]=o),u(u.P+u.F*i,"String",e)},l=s.trim=function(t,n){return t=String(e(t)),1&n&&(t=t.replace(o,"")),2&n&&(t=t.replace(a,"")),t};t.exports=s},function(t,n,r){t.exports={default:r(88),__esModule:!0}},function(t,n,r){t.exports={default:r(89),__esModule:!0}},function(t,n,r){"use strict";function e(t){return t&&t.__esModule?t:{default:t}}n.__esModule=!0;var i=e(r(86)),o=e(r(85)),u="function"==typeof o.default&&"symbol"==typeof i.default?function(t){return typeof t}:function(t){return t&&"function"==typeof o.default&&t.constructor===o.default&&t!==o.default.prototype?"symbol":typeof t};n.default="function"==typeof o.default&&"symbol"===u(i.default)?function(t){return void 0===t?"undefined":u(t)}:function(t){return t&&"function"==typeof o.default&&t.constructor===o.default&&t!==o.default.prototype?"symbol":void 0===t?"undefined":u(t)}},function(t,n,r){r(111),r(109),r(112),r(113),t.exports=r(19).Symbol},function(t,n,r){r(110),r(114),t.exports=r(44).f("iterator")},function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,n){t.exports=function(){}},function(t,n,r){var f=r(15),a=r(107),s=r(106);t.exports=function(c){return function(t,n,r){var e,i=f(t),o=a(i.length),u=s(r,o);if(c&&n!=n){for(;u<o;)if((e=i[u++])!=e)return!0}else for(;u<o;u++)if((c||u in i)&&i[u]===n)return c||u||0;return!c&&-1}}},function(t,n,r){var o=r(90);t.exports=function(e,i,t){if(o(e),void 0===i)return e;switch(t){case 1:return function(t){return e.call(i,t)};case 2:return function(t,n){return e.call(i,t,n)};case 3:return function(t,n,r){return e.call(i,t,n,r)}}return function(){return e.apply(i,arguments)}}},function(t,n,r){var c=r(29),f=r(64),a=r(37);t.exports=function(t){var n=c(t),r=f.f;if(r)for(var e,i=r(t),o=a.f,u=0;i.length>u;)o.call(t,e=i[u++])&&n.push(e);return n}},function(t,n,r){var e=r(6).document;t.exports=e&&e.documentElement},function(t,n,r){var e=r(58);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==e(t)?t.split(""):Object(t)}},function(t,n,r){var e=r(58);t.exports=Array.isArray||function(t){return"Array"==e(t)}},function(t,n,r){"use strict";var e=r(62),i=r(24),o=r(38),u={};r(13)(u,r(16)("iterator"),function(){return this}),t.exports=function(t,n,r){t.prototype=e(u,{next:i(1,r)}),o(t,n+" Iterator")}},function(t,n){t.exports=function(t,n){return{value:n,done:!!t}}},function(t,n,r){var e=r(25)("meta"),i=r(18),o=r(9),u=r(14).f,c=0,f=Object.isExtensible||function(){return!0},a=!r(20)(function(){return f(Object.preventExtensions({}))}),s=function(t){u(t,e,{value:{i:"O"+ ++c,w:{}}})},l=t.exports={KEY:e,NEED:!1,fastKey:function(t,n){if(!i(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!o(t,e)){if(!f(t))return"F";if(!n)return"E";s(t)}return t[e].i},getWeak:function(t,n){if(!o(t,e)){if(!f(t))return!0;if(!n)return!1;s(t)}return t[e].w},onFreeze:function(t){return a&&l.NEED&&f(t)&&!o(t,e)&&s(t),t}}},function(t,n,r){var u=r(14),c=r(22),f=r(29);t.exports=r(12)?Object.defineProperties:function(t,n){c(t);for(var r,e=f(n),i=e.length,o=0;o<i;)u.f(t,r=e[o++],n[r]);return t}},function(t,n,r){var e=r(37),i=r(24),o=r(15),u=r(42),c=r(9),f=r(60),a=Object.getOwnPropertyDescriptor;n.f=r(12)?a:function(t,n){if(t=o(t),n=u(n,!0),f)try{return a(t,n)}catch(t){}if(c(t,n))return i(!e.f.call(t,n),t[n])}},function(t,n,r){var e=r(15),i=r(63).f,o={}.toString,u="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];t.exports.f=function(t){return u&&"[object Window]"==o.call(t)?function(t){try{return i(t)}catch(t){return u.slice()}}(t):i(e(t))}},function(t,n,r){var e=r(9),i=r(55),o=r(39)("IE_PROTO"),u=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=i(t),e(t,o)?t[o]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?u:null}},function(t,n,r){var f=r(41),a=r(34);t.exports=function(c){return function(t,n){var r,e,i=String(a(t)),o=f(n),u=i.length;return o<0||u<=o?c?"":void 0:(r=i.charCodeAt(o))<55296||56319<r||o+1===u||(e=i.charCodeAt(o+1))<56320||57343<e?c?i.charAt(o):r:c?i.slice(o,o+2):e-56320+(r-55296<<10)+65536}}},function(t,n,r){var e=r(41),i=Math.max,o=Math.min;t.exports=function(t,n){return(t=e(t))<0?i(t+n,0):o(t,n)}},function(t,n,r){var e=r(41),i=Math.min;t.exports=function(t){return 0<t?i(e(t),9007199254740991):0}},function(t,n,r){"use strict";var e=r(91),i=r(99),o=r(36),u=r(15);t.exports=r(61)(Array,"Array",function(t,n){this._t=u(t),this._i=0,this._k=n},function(){var t=this._t,n=this._k,r=this._i++;return!t||r>=t.length?(this._t=void 0,i(1)):i(0,"keys"==n?r:"values"==n?t[r]:[r,t[r]])},"values"),o.Arguments=o.Array,e("keys"),e("values"),e("entries")},function(t,n){},function(t,n,r){"use strict";var e=r(105)(!0);r(61)(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,n=this._t,r=this._i;return r>=n.length?{value:void 0,done:!0}:(t=e(n,r),this._i+=t.length,{value:t,done:!1})})},function(t,n,r){"use strict";var e=r(6),u=r(9),i=r(12),o=r(54),c=r(66),f=r(100).KEY,a=r(20),s=r(40),l=r(38),h=r(25),v=r(16),p=r(44),d=r(43),y=r(94),g=r(97),b=r(22),x=r(18),m=r(55),S=r(15),w=r(42),_=r(24),O=r(62),E=r(103),M=r(102),P=r(64),j=r(14),F=r(29),A=M.f,I=j.f,L=E.f,N=e.Symbol,T=e.JSON,k=T&&T.stringify,R="prototype",C=v("_hidden"),D=v("toPrimitive"),G={}.propertyIsEnumerable,W=s("symbol-registry"),U=s("symbols"),V=s("op-symbols"),B=Object[R],q="function"==typeof N&&!!P.f,z=e.QObject,K=!z||!z[R]||!z[R].findChild,H=i&&a(function(){return 7!=O(I({},"a",{get:function(){return I(this,"a",{value:7}).a}})).a})?function(t,n,r){var e=A(B,n);e&&delete B[n],I(t,n,r),e&&t!==B&&I(B,n,e)}:I,J=function(t){var n=U[t]=O(N[R]);return n._k=t,n},$=q&&"symbol"==typeof N.iterator?function(t){return"symbol"==typeof t}:function(t){return t instanceof N},Y=function(t,n,r){return t===B&&Y(V,n,r),b(t),n=w(n,!0),b(r),u(U,n)?(r.enumerable?(u(t,C)&&t[C][n]&&(t[C][n]=!1),r=O(r,{enumerable:_(0,!1)})):(u(t,C)||I(t,C,_(1,{})),t[C][n]=!0),H(t,n,r)):I(t,n,r)},X=function(t,n){b(t);for(var r,e=y(n=S(n)),i=0,o=e.length;i<o;)Y(t,r=e[i++],n[r]);return t},Q=function(t){var n=G.call(this,t=w(t,!0));return!(this===B&&u(U,t)&&!u(V,t))&&(!(n||!u(this,t)||!u(U,t)||u(this,C)&&this[C][t])||n)},Z=function(t,n){if(t=S(t),n=w(n,!0),t!==B||!u(U,n)||u(V,n)){var r=A(t,n);return!r||!u(U,n)||u(t,C)&&t[C][n]||(r.enumerable=!0),r}},tt=function(t){for(var n,r=L(S(t)),e=[],i=0;r.length>i;)u(U,n=r[i++])||n==C||n==f||e.push(n);return e},nt=function(t){for(var n,r=t===B,e=L(r?V:S(t)),i=[],o=0;e.length>o;)!u(U,n=e[o++])||r&&!u(B,n)||i.push(U[n]);return i};q||(c((N=function(){if(this instanceof N)throw TypeError("Symbol is not a constructor!");var n=h(0<arguments.length?arguments[0]:void 0),r=function(t){this===B&&r.call(V,t),u(this,C)&&u(this[C],n)&&(this[C][n]=!1),H(this,n,_(1,t))};return i&&K&&H(B,n,{configurable:!0,set:r}),J(n)})[R],"toString",function(){return this._k}),M.f=Z,j.f=Y,r(63).f=E.f=tt,r(37).f=Q,P.f=nt,i&&!r(23)&&c(B,"propertyIsEnumerable",Q,!0),p.f=function(t){return J(v(t))}),o(o.G+o.W+o.F*!q,{Symbol:N});for(var rt="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),et=0;rt.length>et;)v(rt[et++]);for(var it=F(v.store),ot=0;it.length>ot;)d(it[ot++]);o(o.S+o.F*!q,"Symbol",{for:function(t){return u(W,t+="")?W[t]:W[t]=N(t)},keyFor:function(t){if(!$(t))throw TypeError(t+" is not a symbol!");for(var n in W)if(W[n]===t)return n},useSetter:function(){K=!0},useSimple:function(){K=!1}}),o(o.S+o.F*!q,"Object",{create:function(t,n){return void 0===n?O(t):X(O(t),n)},defineProperty:Y,defineProperties:X,getOwnPropertyDescriptor:Z,getOwnPropertyNames:tt,getOwnPropertySymbols:nt});var ut=a(function(){P.f(1)});o(o.S+o.F*ut,"Object",{getOwnPropertySymbols:function(t){return P.f(m(t))}}),T&&o(o.S+o.F*(!q||a(function(){var t=N();return"[null]"!=k([t])||"{}"!=k({a:t})||"{}"!=k(Object(t))})),"JSON",{stringify:function(t){for(var n,r,e=[t],i=1;arguments.length>i;)e.push(arguments[i++]);if(r=n=e[1],(x(n)||void 0!==t)&&!$(t))return g(n)||(n=function(t,n){if("function"==typeof r&&(n=r.call(this,t,n)),!$(n))return n}),e[1]=n,k.apply(T,e)}}),N[R][D]||r(13)(N[R],D,N[R].valueOf),l(N,"Symbol"),l(Math,"Math",!0),l(e.JSON,"JSON",!0)},function(t,n,r){r(43)("asyncIterator")},function(t,n,r){r(43)("observable")},function(t,n,r){r(108);for(var e=r(6),i=r(13),o=r(36),u=r(16)("toStringTag"),c="CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,TextTrackList,TouchList".split(","),f=0;f<c.length;f++){var a=c[f],s=e[a],l=s&&s.prototype;l&&!l[u]&&i(l,u,a),o[a]=o.Array}},function(t,n,r){"use strict";var e=r(2);t.exports=function(){var t=e(this),n="";return t.global&&(n+="g"),t.ignoreCase&&(n+="i"),t.multiline&&(n+="m"),t.unicode&&(n+="u"),t.sticky&&(n+="y"),n}},function(t,n,r){var e=r(45);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==e(t)?t.split(""):Object(t)}},function(t,n){n.f={}.propertyIsEnumerable},function(t,n,r){var e=r(46),i=r(3),o="__core-js_shared__",u=i[o]||(i[o]={});(t.exports=function(t,n){return u[t]||(u[t]=void 0!==n?n:{})})("versions",[]).push({version:e.version,mode:r(68)?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},function(t,n,r){var i=r(2),o=r(21),u=r(7)("species");t.exports=function(t,n){var r,e=i(t).constructor;return void 0===e||null==(r=i(e)[u])?n:o(r)}},function(t,n,r){var f=r(33),a=r(8),s=r(78);t.exports=function(c){return function(t,n,r){var e,i=f(t),o=a(i.length),u=s(r,o);if(c&&n!=n){for(;u<o;)if((e=i[u++])!=e)return!0}else for(;u<o;u++)if((c||u in i)&&i[u]===n)return c||u||0;return!c&&-1}}},function(t,n,r){"use strict";var g=r(3),b=r(1),x=r(27),m=r(76),S=r(69),w=r(71),_=r(70),O=r(5),E=r(4),M=r(125),P=r(83),j=r(144);t.exports=function(e,t,n,r,i,o){var u=g[e],c=u,f=i?"set":"add",a=c&&c.prototype,s={},l=function(t){var r=a[t];x(a,t,"delete"==t?function(t){return!(o&&!O(t))&&r.call(this,0===t?0:t)}:"has"==t?function(t){return!(o&&!O(t))&&r.call(this,0===t?0:t)}:"get"==t?function(t){return o&&!O(t)?void 0:r.call(this,0===t?0:t)}:"add"==t?function(t){return r.call(this,0===t?0:t),this}:function(t,n){return r.call(this,0===t?0:t,n),this})};if("function"==typeof c&&(o||a.forEach&&!E(function(){(new c).entries().next()}))){var h=new c,v=h[f](o?{}:-0,1)!=h,p=E(function(){h.has(1)}),d=M(function(t){new c(t)}),y=!o&&E(function(){for(var t=new c,n=5;n--;)t[f](n,n);return!t.has(-0)});d||(((c=t(function(t,n){_(t,c,e);var r=j(new u,t,c);return null!=n&&w(n,i,r[f],r),r})).prototype=a).constructor=c),(p||y)&&(l("delete"),l("has"),i&&l("get")),(y||v)&&l(f),o&&a.clear&&delete a.clear}else c=r.getConstructor(t,e,i,f),m(c.prototype,n),S.NEED=!0;return P(c,e),s[e]=c,b(b.G+b.W+b.F*(c!=u),s),o||r.setStrong(c,e,i),c}},function(t,n,r){"use strict";r(197);var s=r(27),l=r(26),h=r(4),v=r(51),p=r(7),d=r(152),y=p("species"),g=!h(function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$<a>")}),b=function(){var t=/(?:)/,n=t.exec;t.exec=function(){return n.apply(this,arguments)};var r="ab".split(t);return 2===r.length&&"a"===r[0]&&"b"===r[1]}();t.exports=function(r,t,n){var e=p(r),o=!h(function(){var t={};return t[e]=function(){return 7},7!=""[r](t)}),i=o?!h(function(){var t=!1,n=/a/;return n.exec=function(){return t=!0,null},"split"===r&&(n.constructor={},n.constructor[y]=function(){return n}),n[e](""),!t}):void 0;if(!o||!i||"replace"===r&&!g||"split"===r&&!b){var u=/./[e],c=n(v,e,""[r],function(t,n,r,e,i){return n.exec===d?o&&!i?{done:!0,value:u.call(n,r,e)}:{done:!0,value:t.call(r,n,e)}:{done:!1}}),f=c[0],a=c[1];s(String.prototype,r,f),l(RegExp.prototype,e,2==t?function(t,n){return a.call(t,this,n)}:function(t){return a.call(t,this)})}}},function(t,n,r){var e=r(45);t.exports=Array.isArray||function(t){return"Array"==e(t)}},function(t,n,r){var e=r(5),i=r(45),o=r(7)("match");t.exports=function(t){var n;return e(t)&&(void 0!==(n=t[o])?!!n:"RegExp"==i(t))}},function(t,n,r){var o=r(7)("iterator"),u=!1;try{var e=[7][o]();e.return=function(){u=!0},Array.from(e,function(){throw 2})}catch(t){}t.exports=function(t,n){if(!n&&!u)return!1;var r=!1;try{var e=[7],i=e[o]();i.next=function(){return{done:r=!0}},e[o]=function(){return i},t(e)}catch(t){}return r}},function(t,n,r){"use strict";t.exports=r(68)||!r(4)(function(){var t=Math.random();__defineSetter__.call(null,t,function(){}),delete r(3)[t]})},function(t,n){n.f=Object.getOwnPropertySymbols},function(t,n,r){"use strict";var i=r(81),o=RegExp.prototype.exec;t.exports=function(t,n){var r=t.exec;if("function"==typeof r){var e=r.call(t,n);if("object"!=typeof e)throw new TypeError("RegExp exec method returned something other than an Object or null");return e}if("RegExp"!==i(t))throw new TypeError("RegExp#exec called on incompatible receiver");return o.call(t,n)}},function(t,n,r){"use strict";var e=r(1),u=r(21),c=r(47),f=r(71);t.exports=function(t){e(e.S,t,{from:function(t){var n,r,e,i,o=arguments[1];return u(this),(n=void 0!==o)&&u(o),null==t?new this:(r=[],n?(e=0,i=c(o,arguments[2],2),f(t,!1,function(t){r.push(i(t,e++))})):f(t,!1,r.push,r),new this(r))}})}},function(t,n,r){"use strict";var e=r(1);t.exports=function(t){e(e.S,t,{of:function(){for(var t=arguments.length,n=new Array(t);t--;)n[t]=arguments[t];return new this(n)}})}},function(t,n,r){var f=r(49),a=r(51);t.exports=function(c){return function(t,n){var r,e,i=String(a(t)),o=f(n),u=i.length;return o<0||u<=o?c?"":void 0:(r=i.charCodeAt(o))<55296||56319<r||o+1===u||(e=i.charCodeAt(o+1))<56320||57343<e?c?i.charAt(o):r:c?i.slice(o,o+2):e-56320+(r-55296<<10)+65536}}},function(t,n,r){for(var e,i=r(3),o=r(26),u=r(79),c=u("typed_array"),f=u("view"),a=!(!i.ArrayBuffer||!i.DataView),s=a,l=0,h="Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array".split(",");l<9;)(e=i[h[l++]])?(o(e.prototype,c,!0),o(e.prototype,f,!0)):s=!1;t.exports={ABV:a,CONSTR:s,TYPED:c,VIEW:f}},function(t,n,r){var e=r(3).navigator;t.exports=e&&e.userAgent||""},function(t,n){"use strict";var r,e={versions:(r=window.navigator.userAgent,{trident:-1<r.indexOf("Trident"),presto:-1<r.indexOf("Presto"),webKit:-1<r.indexOf("AppleWebKit"),gecko:-1<r.indexOf("Gecko")&&-1==r.indexOf("KHTML"),mobile:!!r.match(/AppleWebKit.*Mobile.*/),ios:!!r.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),android:-1<r.indexOf("Android")||-1<r.indexOf("Linux"),iPhone:-1<r.indexOf("iPhone")||-1<r.indexOf("Mac"),iPad:-1<r.indexOf("iPad"),webApp:-1==r.indexOf("Safari"),weixin:-1==r.indexOf("MicroMessenger")})};t.exports=e},function(t,n,r){"use strict";var e,i=r(87),l=(e=i)&&e.__esModule?e:{default:e},h=function(){function n(t,n,r){return n||r?String.fromCharCode(n||r):o[t]||t}function r(t){return s[t]}var e=/&quot;|&lt;|&gt;|&amp;|&nbsp;|&apos;|&#(\d+);|&#(\d+)/g,i=/['<> "&]/g,o={"&quot;":'"',"&lt;":"<","&gt;":">","&amp;":"&","&nbsp;":" "},u=/\u00a0/g,c=/<br\s*\/?>/gi,f=/\r?\n/g,a=/\s/g,s={};for(var t in o)s[o[t]]=t;return o["&apos;"]="'",s["'"]="&#39;",{encode:function(t){return t?(""+t).replace(i,r).replace(f,"<br/>").replace(a,"&nbsp;"):""},decode:function(t){return t?(""+t).replace(c,"\n").replace(e,n).replace(u," "):""},encodeBase16:function(t){if(!t)return t;for(var n=[],r=0,e=(t+="").length;r<e;r++)n.push(t.charCodeAt(r).toString(16).toUpperCase());return n.join("")},encodeBase16forJSON:function(t){if(!t)return t;for(var n=[],r=0,e=(t=t.replace(/[\u4E00-\u9FBF]/gi,function(t){return escape(t).replace("%u","\\u")})).length;r<e;r++)n.push(t.charCodeAt(r).toString(16).toUpperCase());return n.join("")},decodeBase16:function(t){if(!t)return t;for(var n=[],r=0,e=(t+="").length;r<e;r+=2)n.push(String.fromCharCode("0x"+t.slice(r,r+2)));return n.join("")},encodeObject:function(t){if(t instanceof Array)for(var n=0,r=t.length;n<r;n++)t[n]=h.encodeObject(t[n]);else if("object"==(void 0===t?"undefined":(0,l.default)(t)))for(var e in t)t[e]=h.encodeObject(t[e]);else if("string"==typeof t)return h.encode(t);return t},loadScript:function(t){var n=document.createElement("script");document.getElementsByTagName("body")[0].appendChild(n),n.setAttribute("src",t)},addLoadEvent:function(t){var n=window.onload;"function"!=typeof window.onload?window.onload=t:window.onload=function(){n(),t()}}}}();t.exports=h},function(t,n,r){"use strict";var e=r(131)(!0);t.exports=function(t,n,r){return n+(r?e(t,n).length:1)}},function(t,n,r){"use strict";var c=r(17),f=r(78),a=r(8);t.exports=function(t){for(var n=c(this),r=a(n.length),e=arguments.length,i=f(1<e?arguments[1]:void 0,r),o=2<e?arguments[2]:void 0,u=void 0===o?r:f(o,r);i<u;)n[i++]=t;return n}},function(t,n,r){var e=r(215);t.exports=function(t,n){return new(e(t))(n)}},function(t,n,r){"use strict";var e=r(11),i=r(75);t.exports=function(t,n,r){n in t?e.f(t,n,i(0,r)):t[n]=r}},function(t,n,r){var e=r(5),i=r(3).document,o=e(i)&&e(i.createElement);t.exports=function(t){return o?i.createElement(t):{}}},function(t,n){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,n,r){var e=r(7)("match");t.exports=function(n){var r=/./;try{"/./"[n](r)}catch(t){try{return r[e]=!1,!"/./"[n](r)}catch(n){}}return!0}},function(t,n,r){var e=r(3).document;t.exports=e&&e.documentElement},function(t,n,r){var o=r(5),u=r(153).set;t.exports=function(t,n,r){var e,i=n.constructor;return i!==r&&"function"==typeof i&&(e=i.prototype)!==r.prototype&&o(e)&&u&&u(t,e),t}},function(t,n,r){var e=r(82),i=r(7)("iterator"),o=Array.prototype;t.exports=function(t){return void 0!==t&&(e.Array===t||o[i]===t)}},function(t,n,r){"use strict";var e=r(72),i=r(75),o=r(83),u={};r(26)(u,r(7)("iterator"),function(){return this}),t.exports=function(t,n,r){t.prototype=e(u,{next:i(1,r)}),o(t,n+" Iterator")}},function(t,n,r){"use strict";var x=r(68),m=r(1),S=r(27),w=r(26),_=r(82),O=r(146),E=r(83),M=r(32),P=r(7)("iterator"),j=!([].keys&&"next"in[].keys()),F="values",A=function(){return this};t.exports=function(t,n,r,e,i,o,u){O(r,n,e);var c,f,a,s=function(t){if(!j&&t in p)return p[t];switch(t){case"keys":case F:return function(){return new r(this,t)}}return function(){return new r(this,t)}},l=n+" Iterator",h=i==F,v=!1,p=t.prototype,d=p[P]||p["@@iterator"]||i&&p[i],y=d||s(i),g=i?h?s("entries"):y:void 0,b="Array"==n&&p.entries||d;if(b&&((a=M(b.call(new t)))!==Object.prototype&&a.next&&(E(a,l,!0),x||"function"==typeof a[P]||w(a,P,A))),h&&d&&d.name!==F&&(v=!0,y=function(){return d.call(this)}),x&&!u||!j&&!v&&p[P]||w(p,P,y),_[n]=y,_[l]=A,i)if(c={values:h?y:s(F),keys:o?y:s("keys"),entries:g},u)for(f in c)f in p||S(p,f,c[f]);else m(m.P+m.F*(j||v),n,c);return c}},function(t,n){var r=Math.expm1;t.exports=!r||22025.465794806718<r(10)||r(10)<22025.465794806718||-2e-17!=r(-2e-17)?function(t){return 0==(t=+t)?t:-1e-6<t&&t<1e-6?t+t*t/2:Math.exp(t)-1}:r},function(t,n){t.exports=Math.sign||function(t){return 0==(t=+t)||t!=t?t:t<0?-1:1}},function(t,n,r){var c=r(3),f=r(158).set,a=c.MutationObserver||c.WebKitMutationObserver,s=c.process,l=c.Promise,h="process"==r(45)(s);t.exports=function(){var r,e,i,t=function(){var t,n;for(h&&(t=s.domain)&&t.exit();r;){n=r.fn,r=r.next;try{n()}catch(t){throw r?i():e=void 0,t}}e=void 0,t&&t.enter()};if(h)i=function(){s.nextTick(t)};else if(!a||c.navigator&&c.navigator.standalone)if(l&&l.resolve){var n=l.resolve(void 0);i=function(){n.then(t)}}else i=function(){f.call(c,t)};else{var o=!0,u=document.createTextNode("");new a(t).observe(u,{characterData:!0}),i=function(){u.data=o=!o}}return function(t){var n={fn:t,next:void 0};e&&(e.next=n),r||(r=n,i()),e=n}}},function(t,n,r){"use strict";function e(t){var r,e;this.promise=new t(function(t,n){if(void 0!==r||void 0!==e)throw TypeError("Bad Promise constructor");r=t,e=n}),this.resolve=i(r),this.reject=i(e)}var i=r(21);t.exports.f=function(t){return new e(t)}},function(t,n,r){"use strict";var e,i,u=r(115),c=RegExp.prototype.exec,f=String.prototype.replace,o=c,a="lastIndex",s=(e=/a/,i=/b*/g,c.call(e,"a"),c.call(i,"a"),0!==e[a]||0!==i[a]),l=void 0!==/()??/.exec("")[1];(s||l)&&(o=function(t){var n,r,e,i,o=this;return l&&(r=new RegExp("^"+o.source+"$(?!\\s)",u.call(o))),s&&(n=o[a]),e=c.call(o,t),s&&e&&(o[a]=o.global?e.index+e[0].length:n),l&&e&&1<e.length&&f.call(e[0],r,function(){for(i=1;i<arguments.length-2;i++)void 0===arguments[i]&&(e[i]=void 0)}),e}),t.exports=o},function(t,n,i){var r=i(5),e=i(2),o=function(t,n){if(e(t),!r(n)&&null!==n)throw TypeError(n+": can't set as prototype!")};t.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(t,r,e){try{(e=i(47)(Function.call,i(31).f(Object.prototype,"__proto__").set,2))(t,[]),r=!(t instanceof Array)}catch(t){r=!0}return function(t,n){return o(t,n),r?t.__proto__=n:e(t,n),t}}({},!1):void 0),check:o}},function(t,n,r){var e=r(118)("keys"),i=r(79);t.exports=function(t){return e[t]||(e[t]=i(t))}},function(t,n,r){var e=r(124),i=r(51);t.exports=function(t,n,r){if(e(n))throw TypeError("String#"+r+" doesn't accept regex!");return String(i(t))}},function(t,n,r){"use strict";var i=r(49),o=r(51);t.exports=function(t){var n=String(o(this)),r="",e=i(t);if(e<0||e==1/0)throw RangeError("Count can't be negative");for(;0<e;(e>>>=1)&&(n+=n))1&e&&(r+=n);return r}},function(t,n){t.exports="\t\n\v\f\r   ᠎             　\u2028\u2029\ufeff"},function(t,n,r){var e,i,o,u=r(47),c=r(175),f=r(143),a=r(140),s=r(3),l=s.process,h=s.setImmediate,v=s.clearImmediate,p=s.MessageChannel,d=s.Dispatch,y=0,g={},b="onreadystatechange",x=function(){var t=+this;if(g.hasOwnProperty(t)){var n=g[t];delete g[t],n()}},m=function(t){x.call(t.data)};h&&v||(h=function(t){for(var n=[],r=1;arguments.length>r;)n.push(arguments[r++]);return g[++y]=function(){c("function"==typeof t?t:Function(t),n)},e(y),y},v=function(t){delete g[t]},"process"==r(45)(l)?e=function(t){l.nextTick(u(x,t,1))}:d&&d.now?e=function(t){d.now(u(x,t,1))}:p?(o=(i=new p).port2,i.port1.onmessage=m,e=u(o.postMessage,o,1)):s.addEventListener&&"function"==typeof postMessage&&!s.importScripts?(e=function(t){s.postMessage(t+"","*")},s.addEventListener("message",m,!1)):e=b in a("script")?function(t){f.appendChild(a("script"))[b]=function(){f.removeChild(this),x.call(t)}}:function(t){setTimeout(u(x,t,1),0)}),t.exports={set:h,clear:v}},function(t,n,r){"use strict";function e(t,n,r){var e,i,o,u=new Array(r),c=8*r-n-1,f=(1<<c)-1,a=f>>1,s=23===n?W(2,-24)-W(2,-77):0,l=0,h=t<0||0===t&&1/t<0?1:0;for((t=G(t))!=t||t===C?(i=t!=t?1:0,e=f):(e=U(V(t)/B),t*(o=W(2,-e))<1&&(e--,o*=2),2<=(t+=1<=e+a?s/o:s*W(2,1-a))*o&&(e++,o/=2),f<=e+a?(i=0,e=f):1<=e+a?(i=(t*o-1)*W(2,n),e+=a):(i=t*W(2,a-1)*W(2,n),e=0));8<=n;u[l++]=255&i,i/=256,n-=8);for(e=e<<n|i,c+=n;0<c;u[l++]=255&e,e/=256,c-=8);return u[--l]|=128*h,u}function i(t,n,r){var e,i=8*r-n-1,o=(1<<i)-1,u=o>>1,c=i-7,f=r-1,a=t[f--],s=127&a;for(a>>=7;0<c;s=256*s+t[f],f--,c-=8);for(e=s&(1<<-c)-1,s>>=-c,c+=n;0<c;e=256*e+t[f],f--,c-=8);if(0===s)s=1-u;else{if(s===o)return e?NaN:a?-C:C;e+=W(2,n),s-=u}return(a?-1:1)*e*W(2,s-n)}function o(t){return t[3]<<24|t[2]<<16|t[1]<<8|t[0]}function u(t){return[255&t]}function c(t){return[255&t,t>>8&255]}function f(t){return[255&t,t>>8&255,t>>16&255,t>>24&255]}function a(t){return e(t,52,8)}function s(t){return e(t,23,4)}function l(t,n,r){M(t[I],n,{get:function(){return this[r]}})}function h(t,n,r,e){var i=O(+r);if(i+n>t[H])throw R(L);var o=t[K]._b,u=i+t[J],c=o.slice(u,u+n);return e?c:c.reverse()}function v(t,n,r,e,i,o){var u=O(+r);if(u+n>t[H])throw R(L);for(var c=t[K]._b,f=u+t[J],a=e(+i),s=0;s<n;s++)c[f+s]=a[o?s:n-s-1]}var p=r(3),d=r(10),y=r(68),g=r(132),b=r(26),x=r(76),m=r(4),S=r(70),w=r(49),_=r(8),O=r(194),E=r(73).f,M=r(11).f,P=r(137),j=r(83),F="ArrayBuffer",A="DataView",I="prototype",L="Wrong index!",N=p[F],T=p[A],k=p.Math,R=p.RangeError,C=p.Infinity,D=N,G=k.abs,W=k.pow,U=k.floor,V=k.log,B=k.LN2,q="byteLength",z="byteOffset",K=d?"_b":"buffer",H=d?"_l":q,J=d?"_o":z;if(g.ABV){if(!m(function(){N(1)})||!m(function(){new N(-1)})||m(function(){return new N,new N(1.5),new N(NaN),N.name!=F})){for(var $,Y=(N=function(t){return S(this,N),new D(O(t))})[I]=D[I],X=E(D),Q=0;X.length>Q;)($=X[Q++])in N||b(N,$,D[$]);y||(Y.constructor=N)}var Z=new T(new N(2)),tt=T[I].setInt8;Z.setInt8(0,2147483648),Z.setInt8(1,2147483649),!Z.getInt8(0)&&Z.getInt8(1)||x(T[I],{setInt8:function(t,n){tt.call(this,t,n<<24>>24)},setUint8:function(t,n){tt.call(this,t,n<<24>>24)}},!0)}else N=function(t){S(this,N,F);var n=O(t);this._b=P.call(new Array(n),0),this[H]=n},T=function(t,n,r){S(this,T,A),S(t,N,A);var e=t[H],i=w(n);if(i<0||e<i)throw R("Wrong offset!");if(e<i+(r=void 0===r?e-i:_(r)))throw R("Wrong length!");this[K]=t,this[J]=i,this[H]=r},d&&(l(N,q,"_l"),l(T,"buffer","_b"),l(T,q,"_l"),l(T,z,"_o")),x(T[I],{getInt8:function(t){return h(this,1,t)[0]<<24>>24},getUint8:function(t){return h(this,1,t)[0]},getInt16:function(t){var n=h(this,2,t,arguments[1]);return(n[1]<<8|n[0])<<16>>16},getUint16:function(t){var n=h(this,2,t,arguments[1]);return n[1]<<8|n[0]},getInt32:function(t){return o(h(this,4,t,arguments[1]))},getUint32:function(t){return o(h(this,4,t,arguments[1]))>>>0},getFloat32:function(t){return i(h(this,4,t,arguments[1]),23,4)},getFloat64:function(t){return i(h(this,8,t,arguments[1]),52,8)},setInt8:function(t,n){v(this,1,t,u,n)},setUint8:function(t,n){v(this,1,t,u,n)},setInt16:function(t,n){v(this,2,t,c,n,arguments[2])},setUint16:function(t,n){v(this,2,t,c,n,arguments[2])},setInt32:function(t,n){v(this,4,t,f,n,arguments[2])},setUint32:function(t,n){v(this,4,t,f,n,arguments[2])},setFloat32:function(t,n){v(this,4,t,s,n,arguments[2])},setFloat64:function(t,n){v(this,8,t,a,n,arguments[2])}});j(N,F),j(T,A),b(T[I],g.VIEW,!0),n[F]=N,n[A]=T},function(t,n,r){var e=r(3),i=r(46),o=r(68),u=r(195),c=r(11).f;t.exports=function(t){var n=i.Symbol||(i.Symbol=o?{}:e.Symbol||{});"_"==t.charAt(0)||t in n||c(n,t,{value:u.f(t)})}},function(t,n,r){var e=r(81),i=r(7)("iterator"),o=r(82);t.exports=r(46).getIteratorMethod=function(t){if(null!=t)return t[i]||t["@@iterator"]||o[e(t)]}},function(t,n,r){"use strict";var e=r(67),i=r(178),o=r(82),u=r(33);t.exports=r(147)(Array,"Array",function(t,n){this._t=u(t),this._i=0,this._k=n},function(){var t=this._t,n=this._k,r=this._i++;return!t||r>=t.length?(this._t=void 0,i(1)):i(0,"keys"==n?r:"values"==n?t[r]:[r,t[r]])},"values"),o.Arguments=o.Array,e("keys"),e("values"),e("entries")},function(t,n){t.exports=function(t,n){t.classList?t.classList.add(n):t.className+=" "+n}},function(t,n){t.exports=function(t,n){if(t.classList)t.classList.remove(n);else{var r=new RegExp("(^|\\b)"+n.split(" ").join("|")+"(\\b|$)","gi");t.className=t.className.replace(r," ")}}},function(t,n,r){var e=r(45);t.exports=function(t,n){if("number"!=typeof t&&"Number"!=e(t))throw TypeError(n);return+t}},function(t,n,r){"use strict";var a=r(17),s=r(78),l=r(8);t.exports=[].copyWithin||function(t,n){var r=a(this),e=l(r.length),i=s(t,e),o=s(n,e),u=2<arguments.length?arguments[2]:void 0,c=Math.min((void 0===u?e:s(u,e))-o,e-i),f=1;for(o<i&&i<o+c&&(f=-1,o+=c-1,i+=c-1);0<c--;)o in r?r[i]=r[o]:delete r[i],i+=f,o+=f;return r}},function(t,n,r){var e=r(71);t.exports=function(t,n){var r=[];return e(t,!1,r.push,r,n),r}},function(t,n,r){var s=r(21),l=r(17),h=r(116),v=r(8);t.exports=function(t,n,r,e,i){s(n);var o=l(t),u=h(o),c=v(o.length),f=i?c-1:0,a=i?-1:1;if(r<2)for(;;){if(f in u){e=u[f],f+=a;break}if(f+=a,i?f<0:c<=f)throw TypeError("Reduce of empty array with no initial value")}for(;i?0<=f:f<c;f+=a)f in u&&(e=n(e,u[f],f,o));return e}},function(t,n,r){"use strict";var o=r(21),u=r(5),c=r(175),f=[].slice,a={};t.exports=Function.bind||function(n){var r=o(this),e=f.call(arguments,1),i=function(){var t=e.concat(f.call(arguments));return this instanceof i?function(t,n,r){if(!(n in a)){for(var e=[],i=0;i<n;i++)e[i]="a["+i+"]";a[n]=Function("F,a","return new F("+e.join(",")+")")}return a[n](t,r)}(r,t.length,t):c(r,t,n)};return u(r.prototype)&&(i.prototype=r.prototype),i}},function(t,n,r){"use strict";var u=r(11).f,c=r(72),f=r(76),a=r(47),s=r(70),l=r(71),e=r(147),i=r(178),o=r(77),h=r(10),v=r(69).fastKey,p=r(80),d=h?"_s":"size",y=function(t,n){var r,e=v(n);if("F"!==e)return t._i[e];for(r=t._f;r;r=r.n)if(r.k==n)return r};t.exports={getConstructor:function(t,o,r,e){var i=t(function(t,n){s(t,i,o,"_i"),t._t=o,t._i=c(null),t._f=void 0,t._l=void 0,t[d]=0,null!=n&&l(n,r,t[e],t)});return f(i.prototype,{clear:function(){for(var t=p(this,o),n=t._i,r=t._f;r;r=r.n)r.r=!0,r.p&&(r.p=r.p.n=void 0),delete n[r.i];t._f=t._l=void 0,t[d]=0},delete:function(t){var n=p(this,o),r=y(n,t);if(r){var e=r.n,i=r.p;delete n._i[r.i],r.r=!0,i&&(i.n=e),e&&(e.p=i),n._f==r&&(n._f=e),n._l==r&&(n._l=i),n[d]--}return!!r},forEach:function(t){p(this,o);for(var n,r=a(t,1<arguments.length?arguments[1]:void 0,3);n=n?n.n:this._f;)for(r(n.v,n.k,this);n&&n.r;)n=n.p},has:function(t){return!!y(p(this,o),t)}}),h&&u(i.prototype,"size",{get:function(){return p(this,o)[d]}}),i},def:function(t,n,r){var e,i,o=y(t,n);return o?o.v=r:(t._l=o={i:i=v(n,!0),k:n,v:r,p:e=t._l,n:void 0,r:!1},t._f||(t._f=o),e&&(e.n=o),t[d]++,"F"!==i&&(t._i[i]=o)),t},getEntry:y,setStrong:function(t,r,n){e(t,r,function(t,n){this._t=p(t,r),this._k=n,this._l=void 0},function(){for(var t=this,n=t._k,r=t._l;r&&r.r;)r=r.p;return t._t&&(t._l=r=r?r.n:t._t._f)?i(0,"keys"==n?r.k:"values"==n?r.v:[r.k,r.v]):(t._t=void 0,i(1))},n?"entries":"values",!n,!0),o(r)}}},function(t,n,r){var e=r(81),i=r(167);t.exports=function(t){return function(){if(e(this)!=t)throw TypeError(t+"#toJSON isn't generic");return i(this)}}},function(t,n,r){"use strict";var u=r(76),c=r(69).getWeak,i=r(2),f=r(5),a=r(70),s=r(71),e=r(50),l=r(30),h=r(80),o=e(5),v=e(6),p=0,d=function(t){return t._l||(t._l=new y)},y=function(){this.a=[]},g=function(t,n){return o(t.a,function(t){return t[0]===n})};y.prototype={get:function(t){var n=g(this,t);if(n)return n[1]},has:function(t){return!!g(this,t)},set:function(t,n){var r=g(this,t);r?r[1]=n:this.a.push([t,n])},delete:function(n){var t=v(this.a,function(t){return t[0]===n});return~t&&this.a.splice(t,1),!!~t}},t.exports={getConstructor:function(t,r,e,i){var o=t(function(t,n){a(t,o,r,"_i"),t._t=r,t._i=p++,t._l=void 0,null!=n&&s(n,e,t[i],t)});return u(o.prototype,{delete:function(t){if(!f(t))return!1;var n=c(t);return!0===n?d(h(this,r)).delete(t):n&&l(n,this._i)&&delete n[this._i]},has:function(t){if(!f(t))return!1;var n=c(t);return!0===n?d(h(this,r)).has(t):n&&l(n,this._i)}}),o},def:function(t,n,r){var e=c(i(n),!0);return!0===e?d(t).set(n,r):e[t._i]=r,t},ufstore:d}},function(t,n,r){"use strict";var p=r(123),d=r(5),y=r(8),g=r(47),b=r(7)("isConcatSpreadable");t.exports=function t(n,r,e,i,o,u,c,f){for(var a,s,l=o,h=0,v=!!c&&g(c,f,3);h<i;){if(h in e){if(a=v?v(e[h],h,r):e[h],s=!1,d(a)&&(s=void 0!==(s=a[b])?!!s:p(a)),s&&0<u)l=t(n,r,a,y(a.length),l,u-1)-1;else{if(9007199254740991<=l)throw TypeError();n[l]=a}l++}h++}return l}},function(t,n,r){t.exports=!r(10)&&!r(4)(function(){return 7!=Object.defineProperty(r(140)("div"),"a",{get:function(){return 7}}).a})},function(t,n){t.exports=function(t,n,r){var e=void 0===r;switch(n.length){case 0:return e?t():t.call(r);case 1:return e?t(n[0]):t.call(r,n[0]);case 2:return e?t(n[0],n[1]):t.call(r,n[0],n[1]);case 3:return e?t(n[0],n[1],n[2]):t.call(r,n[0],n[1],n[2]);case 4:return e?t(n[0],n[1],n[2],n[3]):t.call(r,n[0],n[1],n[2],n[3])}return t.apply(r,n)}},function(t,n,r){var e=r(5),i=Math.floor;t.exports=function(t){return!e(t)&&isFinite(t)&&i(t)===t}},function(t,n,r){var o=r(2);t.exports=function(t,n,r,e){try{return e?n(o(r)[0],r[1]):n(r)}catch(n){var i=t.return;throw void 0!==i&&o(i.call(t)),n}}},function(t,n){t.exports=function(t,n){return{value:n,done:!!t}}},function(t,n,r){var o=r(149),e=Math.pow,u=e(2,-52),c=e(2,-23),f=e(2,127)*(2-c),a=e(2,-126);t.exports=Math.fround||function(t){var n,r,e=Math.abs(t),i=o(t);return e<a?i*(e/a/c+1/u-1/u)*a*c:f<(r=(n=(1+c/u)*e)-(n-e))||r!=r?i*(1/0):i*r}},function(t,n){t.exports=Math.log1p||function(t){return-1e-8<(t=+t)&&t<1e-8?t-t*t/2:Math.log(1+t)}},function(t,n){t.exports=Math.scale||function(t,n,r,e,i){return 0===arguments.length||t!=t||n!=n||r!=r||e!=e||i!=i?NaN:t===1/0||t===-1/0?t:(t-n)*(i-e)/(r-n)+e}},function(t,n,r){"use strict";var h=r(10),v=r(74),p=r(127),d=r(117),y=r(17),g=r(116),i=Object.assign;t.exports=!i||r(4)(function(){var t={},n={},r=Symbol(),e="abcdefghijklmnopqrst";return t[r]=7,e.split("").forEach(function(t){n[t]=t}),7!=i({},t)[r]||Object.keys(i({},n)).join("")!=e})?function(t,n){for(var r=y(t),e=arguments.length,i=1,o=p.f,u=d.f;i<e;)for(var c,f=g(arguments[i++]),a=o?v(f).concat(o(f)):v(f),s=a.length,l=0;l<s;)c=a[l++],h&&!u.call(f,c)||(r[c]=f[c]);return r}:i},function(t,n,r){var u=r(11),c=r(2),f=r(74);t.exports=r(10)?Object.defineProperties:function(t,n){c(t);for(var r,e=f(n),i=e.length,o=0;o<i;)u.f(t,r=e[o++],n[r]);return t}},function(t,n,r){var e=r(33),i=r(73).f,o={}.toString,u="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];t.exports.f=function(t){return u&&"[object Window]"==o.call(t)?function(t){try{return i(t)}catch(t){return u.slice()}}(t):i(e(t))}},function(t,n,r){var u=r(30),c=r(33),f=r(120)(!1),a=r(154)("IE_PROTO");t.exports=function(t,n){var r,e=c(t),i=0,o=[];for(r in e)r!=a&&u(e,r)&&o.push(r);for(;n.length>i;)u(e,r=n[i++])&&(~f(o,r)||o.push(r));return o}},function(t,n,r){var f=r(10),a=r(74),s=r(33),l=r(117).f;t.exports=function(c){return function(t){for(var n,r=s(t),e=a(r),i=e.length,o=0,u=[];o<i;)n=e[o++],f&&!l.call(r,n)||u.push(c?[n,r[n]]:r[n]);return u}}},function(t,n,r){var e=r(73),i=r(127),o=r(2),u=r(3).Reflect;t.exports=u&&u.ownKeys||function(t){var n=e.f(o(t)),r=i.f;return r?n.concat(r(t)):n}},function(t,n,r){var e=r(3).parseFloat,i=r(84).trim;t.exports=1/e(r(157)+"-0")!=-1/0?function(t){var n=i(String(t),3),r=e(n);return 0===r&&"-"==n.charAt(0)?-0:r}:e},function(t,n,r){var e=r(3).parseInt,i=r(84).trim,o=r(157),u=/^[-+]?0[xX]/;t.exports=8!==e(o+"08")||22!==e(o+"0x16")?function(t,n){var r=i(String(t),3);return e(r,n>>>0||(u.test(r)?16:10))}:e},function(t,n){t.exports=function(t){try{return{e:!1,v:t()}}catch(t){return{e:!0,v:t}}}},function(t,n,r){var e=r(2),i=r(5),o=r(151);t.exports=function(t,n){if(e(t),i(n)&&n.constructor===t)return n;var r=o.f(t);return(0,r.resolve)(n),r.promise}},function(t,n){t.exports=Object.is||function(t,n){return t===n?0!==t||1/t==1/n:t!=t&&n!=n}},function(t,n,r){var s=r(8),l=r(156),h=r(51);t.exports=function(t,n,r,e){var i=String(h(t)),o=i.length,u=void 0===r?" ":String(r),c=s(n);if(c<=o||""==u)return i;var f=c-o,a=l.call(u,Math.ceil(f/u.length));return a.length>f&&(a=a.slice(0,f)),e?a+i:i+a}},function(t,n,r){var e=r(49),i=r(8);t.exports=function(t){if(void 0===t)return 0;var n=e(t),r=i(n);if(n!==r)throw RangeError("Wrong length!");return r}},function(t,n,r){n.f=r(7)},function(t,n,r){"use strict";var e=r(170),i=r(80);t.exports=r(121)("Map",function(t){return function(){return t(this,0<arguments.length?arguments[0]:void 0)}},{get:function(t){var n=e.getEntry(i(this,"Map"),t);return n&&n.v},set:function(t,n){return e.def(i(this,"Map"),0===t?0:t,n)}},e,!0)},function(t,n,r){"use strict";var e=r(152);r(1)({target:"RegExp",proto:!0,forced:e!==/./.exec},{exec:e})},function(t,n,r){r(10)&&"g"!=/./g.flags&&r(11).f(RegExp.prototype,"flags",{configurable:!0,get:r(115)})},function(t,n,r){"use strict";var e=r(170),i=r(80);t.exports=r(121)("Set",function(t){return function(){return t(this,0<arguments.length?arguments[0]:void 0)}},{add:function(t){return e.def(i(this,"Set"),t=0===t?0:t,t)}},e)},function(t,n,r){"use strict";var o,e=r(3),i=r(50)(0),u=r(27),c=r(69),f=r(182),a=r(172),s=r(5),l=r(80),h=r(80),v=!e.ActiveXObject&&"ActiveXObject"in e,p="WeakMap",d=c.getWeak,y=Object.isExtensible,g=a.ufstore,b=function(t){return function(){return t(this,0<arguments.length?arguments[0]:void 0)}},x={get:function(t){if(s(t)){var n=d(t);return!0===n?g(l(this,p)).get(t):n?n[this._i]:void 0}},set:function(t,n){return a.def(l(this,p),t,n)}},m=t.exports=r(121)(p,b,x,a,!0,!0);h&&v&&(f((o=a.getConstructor(b,p)).prototype,x),c.NEED=!0,i(["delete","has","get","set"],function(e){var t=m.prototype,i=t[e];u(t,e,function(t,n){if(!s(t)||y(t))return i.call(this,t,n);this._f||(this._f=new o);var r=this._f[e](t,n);return"set"==e?this:r})}))},,,,function(t,n){"use strict";t.exports={init:function(){var t=document.querySelector("#page-nav");t&&!document.querySelector("#page-nav .extend.prev")&&(t.innerHTML='<a class="extend prev disabled" rel="prev">&laquo; Prev</a>'+t.innerHTML),t&&!document.querySelector("#page-nav .extend.next")&&(t.innerHTML=t.innerHTML+'<a class="extend next disabled" rel="next">Next &raquo;</a>'),yiliaConfig&&yiliaConfig.open_in_new&&document.querySelectorAll(".article-entry a:not(.article-more-a)").forEach(function(t){var n=t.getAttribute("target");n&&""!==n||t.setAttribute("target","_blank")}),yiliaConfig&&yiliaConfig.toc_hide_index&&document.querySelectorAll(".toc-number").forEach(function(t){t.style.display="none"})}}},function(t,n,r){"use strict";function e(t){return t&&t.__esModule?t:{default:t}}function i(t,n,r,e,i){var o=function(t){for(var n=t.offsetLeft,r=t.offsetParent;null!==r;)n+=r.offsetLeft,r=r.offsetParent;return n}(t),u=function(t){for(var n=t.offsetTop,r=t.offsetParent;null!==r;)n+=r.offsetTop,r=r.offsetParent;return n}(t)-n;if(u-r<=i){var c=t.$newDom;c||(c=t.cloneNode(!0),(0,a.default)(t,c),(t.$newDom=c).style.position="fixed",c.style.top=(r||u)+"px",c.style.left=o+"px",c.style.zIndex=e||2,c.style.width="100%",c.style.color="#fff"),c.style.visibility="visible",t.style.visibility="hidden"}else{t.style.visibility="visible";var f=t.$newDom;f&&(f.style.visibility="hidden")}}function o(){var t=document.querySelector(".js-overlay"),n=document.querySelector(".js-header-menu");i(t,document.body.scrollTop,-63,2,0),i(n,document.body.scrollTop,1,3,0)}var f=e(r(163)),a=e((e(r(164)),r(414))),u=e(r(134)),c=e(r(204)),s=r(135);u.default.versions.mobile&&window.screen.width<800&&(function(){for(var t=document.querySelectorAll(".js-header-menu li a"),n=window.location.pathname,r=0,e=t.length;r<e;r++){var i=t[r];o=n,u=i.getAttribute("href"),c=/\/|index.html/g,o.replace(c,"")===u.replace(c,"")&&(0,f.default)(i,"active")}var o,u,c}(),document.querySelector("#container").addEventListener("scroll",function(t){o()}),window.addEventListener("scroll",function(t){o()}),o()),(0,s.addLoadEvent)(function(){c.default.init()}),t.exports={}},,,function(t,n,r){(function(t){"use strict";function n(t,n,r){t[n]||Object.defineProperty(t,n,{writable:!0,configurable:!0,value:r})}if(r(413),r(209),r(211),t._babelPolyfill)throw new Error("only one instance of babel-polyfill is allowed");t._babelPolyfill=!0;n(String.prototype,"padLeft","".padStart),n(String.prototype,"padRight","".padEnd),"pop,reverse,shift,keys,values,entries,indexOf,every,some,forEach,map,filter,find,findIndex,includes,join,slice,concat,push,splice,unshift,sort,lastIndexOf,reduce,reduceRight,copyWithin,fill".split(",").forEach(function(t){[][t]&&n(Array,t,Function.call.bind([][t]))})}).call(n,function(){return this}())},function(L,t){(function(t){!function(t){"use strict";function o(t,n,r,e){var o,u,c,f,i=n&&n.prototype instanceof h?n:h,a=Object.create(i.prototype),s=new p(e||[]);return a._invoke=(o=t,u=r,c=s,f=_,function(t,n){if(f===E)throw new Error("Generator is already running");if(f===M){if("throw"===t)throw n;return d()}for(c.method=t,c.arg=n;;){var r=c.delegate;if(r){var e=v(r,c);if(e){if(e===P)continue;return e}}if("next"===c.method)c.sent=c._sent=c.arg;else if("throw"===c.method){if(f===_)throw f=M,c.arg;c.dispatchException(c.arg)}else"return"===c.method&&c.abrupt("return",c.arg);f=E;var i=l(o,u,c);if("normal"===i.type){if(f=c.done?M:O,i.arg===P)continue;return{value:i.arg,done:c.done}}"throw"===i.type&&(f=M,c.method="throw",c.arg=i.arg)}}),a}function l(t,n,r){try{return{type:"normal",arg:t.call(n,r)}}catch(t){return{type:"throw",arg:t}}}function h(){}function r(){}function n(){}function e(t){["next","throw","return"].forEach(function(n){t[n]=function(t){return this._invoke(n,t)}})}function u(c){function f(t,n,r,e){var i=l(c[t],c,n);if("throw"!==i.type){var o=i.arg,u=o.value;return u&&"object"==typeof u&&y.call(u,"__await")?Promise.resolve(u.__await).then(function(t){f("next",t,r,e)},function(t){f("throw",t,r,e)}):Promise.resolve(u).then(function(t){o.value=t,r(o)},e)}e(i.arg)}var n;"object"==typeof t.process&&t.process.domain&&(f=t.process.domain.bind(f)),this._invoke=function(r,e){function t(){return new Promise(function(t,n){f(r,e,t,n)})}return n=n?n.then(t,t):t()}}function v(t,n){var r=t.iterator[n.method];if(r===a){if(n.delegate=null,"throw"===n.method){if(t.iterator.return&&(n.method="return",n.arg=a,v(t,n),"throw"===n.method))return P;n.method="throw",n.arg=new TypeError("The iterator does not provide a 'throw' method")}return P}var e=l(r,t.iterator,n.arg);if("throw"===e.type)return n.method="throw",n.arg=e.arg,n.delegate=null,P;var i=e.arg;return i?i.done?(n[t.resultName]=i.value,n.next=t.nextLoc,"return"!==n.method&&(n.method="next",n.arg=a),n.delegate=null,P):i:(n.method="throw",n.arg=new TypeError("iterator result is not an object"),n.delegate=null,P)}function i(t){var n={tryLoc:t[0]};1 in t&&(n.catchLoc=t[1]),2 in t&&(n.finallyLoc=t[2],n.afterLoc=t[3]),this.tryEntries.push(n)}function c(t){var n=t.completion||{};n.type="normal",delete n.arg,t.completion=n}function p(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(i,this),this.reset(!0)}function f(n){if(n){var t=n[b];if(t)return t.call(n);if("function"==typeof n.next)return n;if(!isNaN(n.length)){var r=-1,e=function t(){for(;++r<n.length;)if(y.call(n,r))return t.value=n[r],t.done=!1,t;return t.value=a,t.done=!0,t};return e.next=e}}return{next:d}}function d(){return{value:a,done:!0}}var a,s=Object.prototype,y=s.hasOwnProperty,g="function"==typeof Symbol?Symbol:{},b=g.iterator||"@@iterator",x=g.asyncIterator||"@@asyncIterator",m=g.toStringTag||"@@toStringTag",S="object"==typeof L,w=t.regeneratorRuntime;if(w)S&&(L.exports=w);else{(w=t.regeneratorRuntime=S?L.exports:{}).wrap=o;var _="suspendedStart",O="suspendedYield",E="executing",M="completed",P={},j={};j[b]=function(){return this};var F=Object.getPrototypeOf,A=F&&F(F(f([])));A&&A!==s&&y.call(A,b)&&(j=A);var I=n.prototype=h.prototype=Object.create(j);r.prototype=I.constructor=n,n.constructor=r,n[m]=r.displayName="GeneratorFunction",w.isGeneratorFunction=function(t){var n="function"==typeof t&&t.constructor;return!!n&&(n===r||"GeneratorFunction"===(n.displayName||n.name))},w.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,n):(t.__proto__=n,m in t||(t[m]="GeneratorFunction")),t.prototype=Object.create(I),t},w.awrap=function(t){return{__await:t}},e(u.prototype),u.prototype[x]=function(){return this},w.AsyncIterator=u,w.async=function(t,n,r,e){var i=new u(o(t,n,r,e));return w.isGeneratorFunction(n)?i:i.next().then(function(t){return t.done?t.value:i.next()})},e(I),I[m]="Generator",I[b]=function(){return this},I.toString=function(){return"[object Generator]"},w.keys=function(r){var e=[];for(var t in r)e.push(t);return e.reverse(),function t(){for(;e.length;){var n=e.pop();if(n in r)return t.value=n,t.done=!1,t}return t.done=!0,t}},w.values=f,p.prototype={constructor:p,reset:function(t){if(this.prev=0,this.next=0,this.sent=this._sent=a,this.done=!1,this.delegate=null,this.method="next",this.arg=a,this.tryEntries.forEach(c),!t)for(var n in this)"t"===n.charAt(0)&&y.call(this,n)&&!isNaN(+n.slice(1))&&(this[n]=a)},stop:function(){this.done=!0;var t=this.tryEntries[0].completion;if("throw"===t.type)throw t.arg;return this.rval},dispatchException:function(r){function t(t,n){return o.type="throw",o.arg=r,e.next=t,n&&(e.method="next",e.arg=a),!!n}if(this.done)throw r;for(var e=this,n=this.tryEntries.length-1;0<=n;--n){var i=this.tryEntries[n],o=i.completion;if("root"===i.tryLoc)return t("end");if(i.tryLoc<=this.prev){var u=y.call(i,"catchLoc"),c=y.call(i,"finallyLoc");if(u&&c){if(this.prev<i.catchLoc)return t(i.catchLoc,!0);if(this.prev<i.finallyLoc)return t(i.finallyLoc)}else if(u){if(this.prev<i.catchLoc)return t(i.catchLoc,!0)}else{if(!c)throw new Error("try statement without catch or finally");if(this.prev<i.finallyLoc)return t(i.finallyLoc)}}}},abrupt:function(t,n){for(var r=this.tryEntries.length-1;0<=r;--r){var e=this.tryEntries[r];if(e.tryLoc<=this.prev&&y.call(e,"finallyLoc")&&this.prev<e.finallyLoc){var i=e;break}}i&&("break"===t||"continue"===t)&&i.tryLoc<=n&&n<=i.finallyLoc&&(i=null);var o=i?i.completion:{};return o.type=t,o.arg=n,i?(this.method="next",this.next=i.finallyLoc,P):this.complete(o)},complete:function(t,n){if("throw"===t.type)throw t.arg;return"break"===t.type||"continue"===t.type?this.next=t.arg:"return"===t.type?(this.rval=this.arg=t.arg,this.method="return",this.next="end"):"normal"===t.type&&n&&(this.next=n),P},finish:function(t){for(var n=this.tryEntries.length-1;0<=n;--n){var r=this.tryEntries[n];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),c(r),P}},catch:function(t){for(var n=this.tryEntries.length-1;0<=n;--n){var r=this.tryEntries[n];if(r.tryLoc===t){var e=r.completion;if("throw"===e.type){var i=e.arg;c(r)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(t,n,r){return this.delegate={iterator:f(t),resultName:n,nextLoc:r},"next"===this.method&&(this.arg=a),P}}}}("object"==typeof t?t:"object"==typeof window?window:"object"==typeof self?self:this)}).call(t,function(){return this}())},,function(t,n,r){r(221),t.exports=r(46).RegExp.escape},,,,function(t,n,r){var e=r(5),i=r(123),o=r(7)("species");t.exports=function(t){var n;return i(t)&&("function"!=typeof(n=t.constructor)||n!==Array&&!i(n.prototype)||(n=void 0),e(n)&&(null===(n=n[o])&&(n=void 0))),void 0===n?Array:n}},function(t,n,r){"use strict";var e=r(4),i=Date.prototype.getTime,o=Date.prototype.toISOString,u=function(t){return 9<t?t:"0"+t};t.exports=e(function(){return"0385-07-25T07:06:39.999Z"!=o.call(new Date(-5e13-1))})||!e(function(){o.call(new Date(NaN))})?function(){if(!isFinite(i.call(this)))throw RangeError("Invalid time value");var t=this,n=t.getUTCFullYear(),r=t.getUTCMilliseconds(),e=n<0?"-":9999<n?"+":"";return e+("00000"+Math.abs(n)).slice(e?-6:-4)+"-"+u(t.getUTCMonth()+1)+"-"+u(t.getUTCDate())+"T"+u(t.getUTCHours())+":"+u(t.getUTCMinutes())+":"+u(t.getUTCSeconds())+"."+(99<r?r:"0"+u(r))+"Z"}:o},function(t,n,r){"use strict";var e=r(2),i=r(53);t.exports=function(t){if("string"!==t&&"number"!==t&&"default"!==t)throw TypeError("Incorrect hint");return i(e(this),"number"!=t)}},function(t,n,r){var c=r(74),f=r(127),a=r(117);t.exports=function(t){var n=c(t),r=f.f;if(r)for(var e,i=r(t),o=a.f,u=0;i.length>u;)o.call(t,e=i[u++])&&n.push(e);return n}},function(t,n,r){t.exports=r(118)("native-function-to-string",Function.toString)},function(t,n){t.exports=function(n,r){var e=r===Object(r)?function(t){return r[t]}:r;return function(t){return String(t).replace(n,e)}}},function(t,n,r){var e=r(1),i=r(220)(/[\\^$*+?.()|[\]{}]/g,"\\$&");e(e.S,"RegExp",{escape:function(t){return i(t)}})},function(t,n,r){var e=r(1);e(e.P,"Array",{copyWithin:r(166)}),r(67)("copyWithin")},function(t,n,r){"use strict";var e=r(1),i=r(50)(4);e(e.P+e.F*!r(48)([].every,!0),"Array",{every:function(t){return i(this,t,arguments[1])}})},function(t,n,r){var e=r(1);e(e.P,"Array",{fill:r(137)}),r(67)("fill")},function(t,n,r){"use strict";var e=r(1),i=r(50)(2);e(e.P+e.F*!r(48)([].filter,!0),"Array",{filter:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var e=r(1),i=r(50)(6),o="findIndex",u=!0;o in[]&&Array(1)[o](function(){u=!1}),e(e.P+e.F*u,"Array",{findIndex:function(t){return i(this,t,1<arguments.length?arguments[1]:void 0)}}),r(67)(o)},function(t,n,r){"use strict";var e=r(1),i=r(50)(5),o="find",u=!0;o in[]&&Array(1)[o](function(){u=!1}),e(e.P+e.F*u,"Array",{find:function(t){return i(this,t,1<arguments.length?arguments[1]:void 0)}}),r(67)(o)},function(t,n,r){"use strict";var e=r(1),i=r(50)(0),o=r(48)([].forEach,!0);e(e.P+e.F*!o,"Array",{forEach:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var h=r(47),e=r(1),v=r(17),p=r(177),d=r(145),y=r(8),g=r(139),b=r(161);e(e.S+e.F*!r(125)(function(t){Array.from(t)}),"Array",{from:function(t){var n,r,e,i,o=v(t),u="function"==typeof this?this:Array,c=arguments.length,f=1<c?arguments[1]:void 0,a=void 0!==f,s=0,l=b(o);if(a&&(f=h(f,2<c?arguments[2]:void 0,2)),null==l||u==Array&&d(l))for(r=new u(n=y(o.length));s<n;s++)g(r,s,a?f(o[s],s):o[s]);else for(i=l.call(o),r=new u;!(e=i.next()).done;s++)g(r,s,a?p(i,f,[e.value,s],!0):e.value);return r.length=s,r}})},function(t,n,r){"use strict";var e=r(1),i=r(120)(!1),o=[].indexOf,u=!!o&&1/[1].indexOf(1,-0)<0;e(e.P+e.F*(u||!r(48)(o)),"Array",{indexOf:function(t){return u?o.apply(this,arguments)||0:i(this,t,arguments[1])}})},function(t,n,r){var e=r(1);e(e.S,"Array",{isArray:r(123)})},function(t,n,r){"use strict";var e=r(1),i=r(33),o=[].join;e(e.P+e.F*(r(116)!=Object||!r(48)(o)),"Array",{join:function(t){return o.call(i(this),void 0===t?",":t)}})},function(t,n,r){"use strict";var e=r(1),i=r(33),o=r(49),u=r(8),c=[].lastIndexOf,f=!!c&&1/[1].lastIndexOf(1,-0)<0;e(e.P+e.F*(f||!r(48)(c)),"Array",{lastIndexOf:function(t){if(f)return c.apply(this,arguments)||0;var n=i(this),r=u(n.length),e=r-1;for(1<arguments.length&&(e=Math.min(e,o(arguments[1]))),e<0&&(e=r+e);0<=e;e--)if(e in n&&n[e]===t)return e||0;return-1}})},function(t,n,r){"use strict";var e=r(1),i=r(50)(1);e(e.P+e.F*!r(48)([].map,!0),"Array",{map:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var e=r(1),i=r(139);e(e.S+e.F*r(4)(function(){function t(){}return!(Array.of.call(t)instanceof t)}),"Array",{of:function(){for(var t=0,n=arguments.length,r=new("function"==typeof this?this:Array)(n);t<n;)i(r,t,arguments[t++]);return r.length=n,r}})},function(t,n,r){"use strict";var e=r(1),i=r(168);e(e.P+e.F*!r(48)([].reduceRight,!0),"Array",{reduceRight:function(t){return i(this,t,arguments.length,arguments[1],!0)}})},function(t,n,r){"use strict";var e=r(1),i=r(168);e(e.P+e.F*!r(48)([].reduce,!0),"Array",{reduce:function(t){return i(this,t,arguments.length,arguments[1],!1)}})},function(t,n,r){"use strict";var e=r(1),i=r(143),a=r(45),s=r(78),l=r(8),h=[].slice;e(e.P+e.F*r(4)(function(){i&&h.call(i)}),"Array",{slice:function(t,n){var r=l(this.length),e=a(this);if(n=void 0===n?r:n,"Array"==e)return h.call(this,t,n);for(var i=s(t,r),o=s(n,r),u=l(o-i),c=new Array(u),f=0;f<u;f++)c[f]="String"==e?this.charAt(i+f):this[i+f];return c}})},function(t,n,r){"use strict";var e=r(1),i=r(50)(3);e(e.P+e.F*!r(48)([].some,!0),"Array",{some:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var e=r(1),i=r(21),o=r(17),u=r(4),c=[].sort,f=[1,2,3];e(e.P+e.F*(u(function(){f.sort(void 0)})||!u(function(){f.sort(null)})||!r(48)(c)),"Array",{sort:function(t){return void 0===t?c.call(o(this)):c.call(o(this),i(t))}})},function(t,n,r){r(77)("Array")},function(t,n,r){var e=r(1);e(e.S,"Date",{now:function(){return(new Date).getTime()}})},function(t,n,r){var e=r(1),i=r(216);e(e.P+e.F*(Date.prototype.toISOString!==i),"Date",{toISOString:i})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(53);e(e.P+e.F*r(4)(function(){return null!==new Date(NaN).toJSON()||1!==Date.prototype.toJSON.call({toISOString:function(){return 1}})}),"Date",{toJSON:function(t){var n=i(this),r=o(n);return"number"!=typeof r||isFinite(r)?n.toISOString():null}})},function(t,n,r){var e=r(7)("toPrimitive"),i=Date.prototype;e in i||r(26)(i,e,r(217))},function(t,n,r){var e=Date.prototype,i="Invalid Date",o="toString",u=e[o],c=e.getTime;new Date(NaN)+""!=i&&r(27)(e,o,function(){var t=c.call(this);return t==t?u.call(this):i})},function(t,n,r){var e=r(1);e(e.P,"Function",{bind:r(169)})},function(t,n,r){"use strict";var e=r(5),i=r(32),o=r(7)("hasInstance"),u=Function.prototype;o in u||r(11).f(u,o,{value:function(t){if("function"!=typeof this||!e(t))return!1;if(!e(this.prototype))return t instanceof this;for(;t=i(t);)if(this.prototype===t)return!0;return!1}})},function(t,n,r){var e=r(11).f,i=Function.prototype,o=/^\s*function ([^ (]*)/;"name"in i||r(10)&&e(i,"name",{configurable:!0,get:function(){try{return(""+this).match(o)[1]}catch(t){return""}}})},function(t,n,r){var e=r(1),i=r(180),o=Math.sqrt,u=Math.acosh;e(e.S+e.F*!(u&&710==Math.floor(u(Number.MAX_VALUE))&&u(1/0)==1/0),"Math",{acosh:function(t){return(t=+t)<1?NaN:94906265.62425156<t?Math.log(t)+Math.LN2:i(t-1+o(t-1)*o(t+1))}})},function(t,n,r){var e=r(1),i=Math.asinh;e(e.S+e.F*!(i&&0<1/i(0)),"Math",{asinh:function t(n){return isFinite(n=+n)&&0!=n?n<0?-t(-n):Math.log(n+Math.sqrt(n*n+1)):n}})},function(t,n,r){var e=r(1),i=Math.atanh;e(e.S+e.F*!(i&&1/i(-0)<0),"Math",{atanh:function(t){return 0==(t=+t)?t:Math.log((1+t)/(1-t))/2}})},function(t,n,r){var e=r(1),i=r(149);e(e.S,"Math",{cbrt:function(t){return i(t=+t)*Math.pow(Math.abs(t),1/3)}})},function(t,n,r){var e=r(1);e(e.S,"Math",{clz32:function(t){return(t>>>=0)?31-Math.floor(Math.log(t+.5)*Math.LOG2E):32}})},function(t,n,r){var e=r(1),i=Math.exp;e(e.S,"Math",{cosh:function(t){return(i(t=+t)+i(-t))/2}})},function(t,n,r){var e=r(1),i=r(148);e(e.S+e.F*(i!=Math.expm1),"Math",{expm1:i})},function(t,n,r){var e=r(1);e(e.S,"Math",{fround:r(179)})},function(t,n,r){var e=r(1),f=Math.abs;e(e.S,"Math",{hypot:function(t,n){for(var r,e,i=0,o=0,u=arguments.length,c=0;o<u;)c<(r=f(arguments[o++]))?(i=i*(e=c/r)*e+1,c=r):0<r?i+=(e=r/c)*e:i+=r;return c===1/0?1/0:c*Math.sqrt(i)}})},function(t,n,r){var e=r(1),i=Math.imul;e(e.S+e.F*r(4)(function(){return-5!=i(4294967295,5)||2!=i.length}),"Math",{imul:function(t,n){var r=65535,e=+t,i=+n,o=r&e,u=r&i;return 0|o*u+((r&e>>>16)*u+o*(r&i>>>16)<<16>>>0)}})},function(t,n,r){var e=r(1);e(e.S,"Math",{log10:function(t){return Math.log(t)*Math.LOG10E}})},function(t,n,r){var e=r(1);e(e.S,"Math",{log1p:r(180)})},function(t,n,r){var e=r(1);e(e.S,"Math",{log2:function(t){return Math.log(t)/Math.LN2}})},function(t,n,r){var e=r(1);e(e.S,"Math",{sign:r(149)})},function(t,n,r){var e=r(1),i=r(148),o=Math.exp;e(e.S+e.F*r(4)(function(){return-2e-17!=!Math.sinh(-2e-17)}),"Math",{sinh:function(t){return Math.abs(t=+t)<1?(i(t)-i(-t))/2:(o(t-1)-o(-t-1))*(Math.E/2)}})},function(t,n,r){var e=r(1),i=r(148),o=Math.exp;e(e.S,"Math",{tanh:function(t){var n=i(t=+t),r=i(-t);return n==1/0?1:r==1/0?-1:(n-r)/(o(t)+o(-t))}})},function(t,n,r){var e=r(1);e(e.S,"Math",{trunc:function(t){return(0<t?Math.floor:Math.ceil)(t)}})},function(t,n,r){"use strict";var e=r(3),i=r(30),o=r(45),u=r(144),s=r(53),c=r(4),f=r(73).f,a=r(31).f,l=r(11).f,h=r(84).trim,v="Number",p=e[v],d=p,y=p.prototype,g=o(r(72)(y))==v,b="trim"in String.prototype,x=function(t){var n=s(t,!1);if("string"==typeof n&&2<n.length){var r,e,i,o=(n=b?n.trim():h(n,3)).charCodeAt(0);if(43===o||45===o){if(88===(r=n.charCodeAt(2))||120===r)return NaN}else if(48===o){switch(n.charCodeAt(1)){case 66:case 98:e=2,i=49;break;case 79:case 111:e=8,i=55;break;default:return+n}for(var u,c=n.slice(2),f=0,a=c.length;f<a;f++)if((u=c.charCodeAt(f))<48||i<u)return NaN;return parseInt(c,e)}}return+n};if(!p(" 0o1")||!p("0b1")||p("+0x1")){p=function(t){var n=arguments.length<1?0:t,r=this;return r instanceof p&&(g?c(function(){y.valueOf.call(r)}):o(r)!=v)?u(new d(x(n)),r,p):x(n)};for(var m,S=r(10)?f(d):"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger".split(","),w=0;S.length>w;w++)i(d,m=S[w])&&!i(p,m)&&l(p,m,a(d,m));(p.prototype=y).constructor=p,r(27)(e,v,p)}},function(t,n,r){var e=r(1);e(e.S,"Number",{EPSILON:Math.pow(2,-52)})},function(t,n,r){var e=r(1),i=r(3).isFinite;e(e.S,"Number",{isFinite:function(t){return"number"==typeof t&&i(t)}})},function(t,n,r){var e=r(1);e(e.S,"Number",{isInteger:r(176)})},function(t,n,r){var e=r(1);e(e.S,"Number",{isNaN:function(t){return t!=t}})},function(t,n,r){var e=r(1),i=r(176),o=Math.abs;e(e.S,"Number",{isSafeInteger:function(t){return i(t)&&o(t)<=9007199254740991}})},function(t,n,r){var e=r(1);e(e.S,"Number",{MAX_SAFE_INTEGER:9007199254740991})},function(t,n,r){var e=r(1);e(e.S,"Number",{MIN_SAFE_INTEGER:-9007199254740991})},function(t,n,r){var e=r(1),i=r(188);e(e.S+e.F*(Number.parseFloat!=i),"Number",{parseFloat:i})},function(t,n,r){var e=r(1),i=r(189);e(e.S+e.F*(Number.parseInt!=i),"Number",{parseInt:i})},function(t,n,r){"use strict";var e=r(1),a=r(49),s=r(165),l=r(156),i=1..toFixed,o=Math.floor,u=[0,0,0,0,0,0],h="Number.toFixed: incorrect invocation!",v=function(t,n){for(var r=-1,e=n;++r<6;)e+=t*u[r],u[r]=e%1e7,e=o(e/1e7)},p=function(t){for(var n=6,r=0;0<=--n;)r+=u[n],u[n]=o(r/t),r=r%t*1e7},d=function(){for(var t=6,n="";0<=--t;)if(""!==n||0===t||0!==u[t]){var r=String(u[t]);n=""===n?r:n+l.call("0",7-r.length)+r}return n},y=function(t,n,r){return 0===n?r:n%2==1?y(t,n-1,r*t):y(t*t,n/2,r)};e(e.P+e.F*(!!i&&("0.000"!==8e-5.toFixed(3)||"1"!==.9.toFixed(0)||"1.25"!==1.255.toFixed(2)||"1000000000000000128"!==(0xde0b6b3a7640080).toFixed(0))||!r(4)(function(){i.call({})})),"Number",{toFixed:function(t){var n,r,e,i,o=s(this,h),u=a(t),c="",f="0";if(u<0||20<u)throw RangeError(h);if(o!=o)return"NaN";if(o<=-1e21||1e21<=o)return String(o);if(o<0&&(c="-",o=-o),1e-21<o)if(r=(n=function(t){for(var n=0,r=t;4096<=r;)n+=12,r/=4096;for(;2<=r;)n+=1,r/=2;return n}(o*y(2,69,1))-69)<0?o*y(2,-n,1):o/y(2,n,1),r*=4503599627370496,0<(n=52-n)){for(v(0,r),e=u;7<=e;)v(1e7,0),e-=7;for(v(y(10,e,1),0),e=n-1;23<=e;)p(1<<23),e-=23;p(1<<e),v(1,1),p(2),f=d()}else v(0,r),v(1<<-n,0),f=d()+l.call("0",u);return f=0<u?c+((i=f.length)<=u?"0."+l.call("0",u-i)+f:f.slice(0,i-u)+"."+f.slice(i-u)):c+f}})},function(t,n,r){"use strict";var e=r(1),i=r(4),o=r(165),u=1..toPrecision;e(e.P+e.F*(i(function(){return"1"!==u.call(1,void 0)})||!i(function(){u.call({})})),"Number",{toPrecision:function(t){var n=o(this,"Number#toPrecision: incorrect invocation!");return void 0===t?u.call(n):u.call(n,t)}})},function(t,n,r){var e=r(1);e(e.S+e.F,"Object",{assign:r(182)})},function(t,n,r){var e=r(1);e(e.S,"Object",{create:r(72)})},function(t,n,r){var e=r(1);e(e.S+e.F*!r(10),"Object",{defineProperties:r(183)})},function(t,n,r){var e=r(1);e(e.S+e.F*!r(10),"Object",{defineProperty:r(11).f})},function(t,n,r){var e=r(5),i=r(69).onFreeze;r(52)("freeze",function(n){return function(t){return n&&e(t)?n(i(t)):t}})},function(t,n,r){var e=r(33),i=r(31).f;r(52)("getOwnPropertyDescriptor",function(){return function(t,n){return i(e(t),n)}})},function(t,n,r){r(52)("getOwnPropertyNames",function(){return r(184).f})},function(t,n,r){var e=r(17),i=r(32);r(52)("getPrototypeOf",function(){return function(t){return i(e(t))}})},function(t,n,r){var e=r(5);r(52)("isExtensible",function(n){return function(t){return!!e(t)&&(!n||n(t))}})},function(t,n,r){var e=r(5);r(52)("isFrozen",function(n){return function(t){return!e(t)||!!n&&n(t)}})},function(t,n,r){var e=r(5);r(52)("isSealed",function(n){return function(t){return!e(t)||!!n&&n(t)}})},function(t,n,r){var e=r(1);e(e.S,"Object",{is:r(192)})},function(t,n,r){var e=r(17),i=r(74);r(52)("keys",function(){return function(t){return i(e(t))}})},function(t,n,r){var e=r(5),i=r(69).onFreeze;r(52)("preventExtensions",function(n){return function(t){return n&&e(t)?n(i(t)):t}})},function(t,n,r){var e=r(5),i=r(69).onFreeze;r(52)("seal",function(n){return function(t){return n&&e(t)?n(i(t)):t}})},function(t,n,r){var e=r(1);e(e.S,"Object",{setPrototypeOf:r(153).set})},function(t,n,r){"use strict";var e=r(81),i={};i[r(7)("toStringTag")]="z",i+""!="[object z]"&&r(27)(Object.prototype,"toString",function(){return"[object "+e(this)+"]"},!0)},function(t,n,r){var e=r(1),i=r(188);e(e.G+e.F*(parseFloat!=i),{parseFloat:i})},function(t,n,r){var e=r(1),i=r(189);e(e.G+e.F*(parseInt!=i),{parseInt:i})},function(t,n,r){"use strict";var e,i,o,u,c=r(68),f=r(3),a=r(47),s=r(81),l=r(1),h=r(5),v=r(21),p=r(70),d=r(71),y=r(119),g=r(158).set,b=r(150)(),x=r(151),m=r(190),S=r(133),w=r(191),_="Promise",O=f.TypeError,E=f.process,M=E&&E.versions,P=M&&M.v8||"",j=f[_],F="process"==s(E),A=function(){},I=i=x.f,L=!!function(){try{var t=j.resolve(1),n=(t.constructor={})[r(7)("species")]=function(t){t(A,A)};return(F||"function"==typeof PromiseRejectionEvent)&&t.then(A)instanceof n&&0!==P.indexOf("6.6")&&-1===S.indexOf("Chrome/66")}catch(t){}}(),N=function(t){var n;return!(!h(t)||"function"!=typeof(n=t.then))&&n},T=function(s,r){if(!s._n){s._n=!0;var e=s._c;b(function(){for(var f=s._v,a=1==s._s,t=0,n=function(t){var n,r,e,i=a?t.ok:t.fail,o=t.resolve,u=t.reject,c=t.domain;try{i?(a||(2==s._h&&C(s),s._h=1),!0===i?n=f:(c&&c.enter(),n=i(f),c&&(c.exit(),e=!0)),n===t.promise?u(O("Promise-chain cycle")):(r=N(n))?r.call(n,o,u):o(n)):u(f)}catch(t){c&&!e&&c.exit(),u(t)}};e.length>t;)n(e[t++]);s._c=[],s._n=!1,r&&!s._h&&k(s)})}},k=function(o){g.call(f,function(){var t,n,r,e=o._v,i=R(o);if(i&&(t=m(function(){F?E.emit("unhandledRejection",e,o):(n=f.onunhandledrejection)?n({promise:o,reason:e}):(r=f.console)&&r.error&&r.error("Unhandled promise rejection",e)}),o._h=F||R(o)?2:1),o._a=void 0,i&&t.e)throw t.v})},R=function(t){return 1!==t._h&&0===(t._a||t._c).length},C=function(n){g.call(f,function(){var t;F?E.emit("rejectionHandled",n):(t=f.onrejectionhandled)&&t({promise:n,reason:n._v})})},D=function(t){var n=this;n._d||(n._d=!0,(n=n._w||n)._v=t,n._s=2,n._a||(n._a=n._c.slice()),T(n,!0))},G=function(t){var r,e=this;if(!e._d){e._d=!0,e=e._w||e;try{if(e===t)throw O("Promise can't be resolved itself");(r=N(t))?b(function(){var n={_w:e,_d:!1};try{r.call(t,a(G,n,1),a(D,n,1))}catch(t){D.call(n,t)}}):(e._v=t,e._s=1,T(e,!1))}catch(t){D.call({_w:e,_d:!1},t)}}};L||(j=function(t){p(this,j,_,"_h"),v(t),e.call(this);try{t(a(G,this,1),a(D,this,1))}catch(t){D.call(this,t)}},(e=function(t){this._c=[],this._a=void 0,this._s=0,this._d=!1,this._v=void 0,this._h=0,this._n=!1}).prototype=r(76)(j.prototype,{then:function(t,n){var r=I(y(this,j));return r.ok="function"!=typeof t||t,r.fail="function"==typeof n&&n,r.domain=F?E.domain:void 0,this._c.push(r),this._a&&this._a.push(r),this._s&&T(this,!1),r.promise},catch:function(t){return this.then(void 0,t)}}),o=function(){var t=new e;this.promise=t,this.resolve=a(G,t,1),this.reject=a(D,t,1)},x.f=I=function(t){return t===j||t===u?new o(t):i(t)}),l(l.G+l.W+l.F*!L,{Promise:j}),r(83)(j,_),r(77)(_),u=r(46)[_],l(l.S+l.F*!L,_,{reject:function(t){var n=I(this);return(0,n.reject)(t),n.promise}}),l(l.S+l.F*(c||!L),_,{resolve:function(t){return w(c&&this===u?j:this,t)}}),l(l.S+l.F*!(L&&r(125)(function(t){j.all(t).catch(A)})),_,{all:function(t){var u=this,n=I(u),c=n.resolve,f=n.reject,r=m(function(){var e=[],i=0,o=1;d(t,!1,function(t){var n=i++,r=!1;e.push(void 0),o++,u.resolve(t).then(function(t){r||(r=!0,e[n]=t,--o||c(e))},f)}),--o||c(e)});return r.e&&f(r.v),n.promise},race:function(t){var n=this,r=I(n),e=r.reject,i=m(function(){d(t,!1,function(t){n.resolve(t).then(r.resolve,e)})});return i.e&&e(i.v),r.promise}})},function(t,n,r){var e=r(1),o=r(21),u=r(2),c=(r(3).Reflect||{}).apply,f=Function.apply;e(e.S+e.F*!r(4)(function(){c(function(){})}),"Reflect",{apply:function(t,n,r){var e=o(t),i=u(r);return c?c(e,n,i):f.call(e,n,i)}})},function(t,n,r){var e=r(1),c=r(72),f=r(21),a=r(2),s=r(5),i=r(4),l=r(169),h=(r(3).Reflect||{}).construct,v=i(function(){function t(){}return!(h(function(){},[],t)instanceof t)}),p=!i(function(){h(function(){})});e(e.S+e.F*(v||p),"Reflect",{construct:function(t,n){f(t),a(n);var r=arguments.length<3?t:f(arguments[2]);if(p&&!v)return h(t,n,r);if(t==r){switch(n.length){case 0:return new t;case 1:return new t(n[0]);case 2:return new t(n[0],n[1]);case 3:return new t(n[0],n[1],n[2]);case 4:return new t(n[0],n[1],n[2],n[3])}var e=[null];return e.push.apply(e,n),new(l.apply(t,e))}var i=r.prototype,o=c(s(i)?i:Object.prototype),u=Function.apply.call(t,o,n);return s(u)?u:o}})},function(t,n,r){var e=r(11),i=r(1),o=r(2),u=r(53);i(i.S+i.F*r(4)(function(){Reflect.defineProperty(e.f({},1,{value:1}),1,{value:2})}),"Reflect",{defineProperty:function(t,n,r){o(t),n=u(n,!0),o(r);try{return e.f(t,n,r),!0}catch(t){return!1}}})},function(t,n,r){var e=r(1),i=r(31).f,o=r(2);e(e.S,"Reflect",{deleteProperty:function(t,n){var r=i(o(t),n);return!(r&&!r.configurable)&&delete t[n]}})},function(t,n,r){"use strict";var e=r(1),i=r(2),o=function(t){this._t=i(t),this._i=0;var n,r=this._k=[];for(n in t)r.push(n)};r(146)(o,"Object",function(){var t,n=this._k;do{if(this._i>=n.length)return{value:void 0,done:!0}}while(!((t=n[this._i++])in this._t));return{value:t,done:!1}}),e(e.S,"Reflect",{enumerate:function(t){return new o(t)}})},function(t,n,r){var e=r(31),i=r(1),o=r(2);i(i.S,"Reflect",{getOwnPropertyDescriptor:function(t,n){return e.f(o(t),n)}})},function(t,n,r){var e=r(1),i=r(32),o=r(2);e(e.S,"Reflect",{getPrototypeOf:function(t){return i(o(t))}})},function(t,n,r){var u=r(31),c=r(32),f=r(30),e=r(1),a=r(5),s=r(2);e(e.S,"Reflect",{get:function t(n,r){var e,i,o=arguments.length<3?n:arguments[2];return s(n)===o?n[r]:(e=u.f(n,r))?f(e,"value")?e.value:void 0!==e.get?e.get.call(o):void 0:a(i=c(n))?t(i,r,o):void 0}})},function(t,n,r){var e=r(1);e(e.S,"Reflect",{has:function(t,n){return n in t}})},function(t,n,r){var e=r(1),i=r(2),o=Object.isExtensible;e(e.S,"Reflect",{isExtensible:function(t){return i(t),!o||o(t)}})},function(t,n,r){var e=r(1);e(e.S,"Reflect",{ownKeys:r(187)})},function(t,n,r){var e=r(1),i=r(2),o=Object.preventExtensions;e(e.S,"Reflect",{preventExtensions:function(t){i(t);try{return o&&o(t),!0}catch(t){return!1}}})},function(t,n,r){var e=r(1),i=r(153);i&&e(e.S,"Reflect",{setPrototypeOf:function(t,n){i.check(t,n);try{return i.set(t,n),!0}catch(t){return!1}}})},function(t,n,r){var f=r(11),a=r(31),s=r(32),l=r(30),e=r(1),h=r(75),v=r(2),p=r(5);e(e.S,"Reflect",{set:function t(n,r,e){var i,o,u=arguments.length<4?n:arguments[3],c=a.f(v(n),r);if(!c){if(p(o=s(n)))return t(o,r,e,u);c=h(0)}if(l(c,"value")){if(!1===c.writable||!p(u))return!1;if(i=a.f(u,r)){if(i.get||i.set||!1===i.writable)return!1;i.value=e,f.f(u,r,i)}else f.f(u,r,h(0,e));return!0}return void 0!==c.set&&(c.set.call(u,e),!0)}})},function(t,n,r){var e=r(3),o=r(144),i=r(11).f,u=r(73).f,c=r(124),f=r(115),a=e.RegExp,s=a,l=a.prototype,h=/a/g,v=/a/g,p=new a(h)!==h;if(r(10)&&(!p||r(4)(function(){return v[r(7)("match")]=!1,a(h)!=h||a(v)==v||"/a/i"!=a(h,"i")}))){a=function(t,n){var r=this instanceof a,e=c(t),i=void 0===n;return!r&&e&&t.constructor===a&&i?t:o(p?new s(e&&!i?t.source:t,n):s((e=t instanceof a)?t.source:t,e&&i?f.call(t):n),r?this:l,a)};for(var d=function(n){n in a||i(a,n,{configurable:!0,get:function(){return s[n]},set:function(t){s[n]=t}})},y=u(s),g=0;y.length>g;)d(y[g++]);(l.constructor=a).prototype=l,r(27)(e,"RegExp",a)}r(77)("RegExp")},function(t,n,r){"use strict";var l=r(2),h=r(8),v=r(136),p=r(128);r(122)("match",1,function(e,i,a,s){return[function(t){var n=e(this),r=null==t?void 0:t[i];return void 0!==r?r.call(t,n):new RegExp(t)[i](String(n))},function(t){var n=s(a,t,this);if(n.done)return n.value;var r=l(t),e=String(this);if(!r.global)return p(r,e);for(var i,o=r.unicode,u=[],c=r.lastIndex=0;null!==(i=p(r,e));){var f=String(i[0]);""===(u[c]=f)&&(r.lastIndex=v(e,h(r.lastIndex),o)),c++}return 0===c?null:u}]})},function(t,n,r){"use strict";var O=r(2),e=r(17),E=r(8),M=r(49),P=r(136),j=r(128),F=Math.max,A=Math.min,h=Math.floor,v=/\$([$&`']|\d\d?|<[^>]*>)/g,p=/\$([$&`']|\d\d?)/g;r(122)("replace",2,function(i,o,S,w){function _(o,u,c,f,a,t){var s=c+o.length,l=f.length,n=p;return void 0!==a&&(a=e(a),n=v),S.call(t,n,function(t,n){var r;switch(n.charAt(0)){case"$":return"$";case"&":return o;case"`":return u.slice(0,c);case"'":return u.slice(s);case"<":r=a[n.slice(1,-1)];break;default:var e=+n;if(0===e)return t;if(l<e){var i=h(e/10);return 0===i?t:i<=l?void 0===f[i-1]?n.charAt(1):f[i-1]+n.charAt(1):t}r=f[e-1]}return void 0===r?"":r})}return[function(t,n){var r=i(this),e=null==t?void 0:t[o];return void 0!==e?e.call(t,r,n):S.call(String(r),t,n)},function(t,n){var r=w(S,t,this,n);if(r.done)return r.value;var e=O(t),i=String(this),o="function"==typeof n;o||(n=String(n));var u,c=e.global;if(c){var f=e.unicode;e.lastIndex=0}for(var a=[];;){var s=j(e,i);if(null===s)break;if(a.push(s),!c)break;""===String(s[0])&&(e.lastIndex=P(i,E(e.lastIndex),f))}for(var l="",h=0,v=0;v<a.length;v++){s=a[v];for(var p=String(s[0]),d=F(A(M(s.index),i.length),0),y=[],g=1;g<s.length;g++)y.push(void 0===(u=s[g])?u:String(u));var b=s.groups;if(o){var x=[p].concat(y,d,i);void 0!==b&&x.push(b);var m=String(n.apply(void 0,x))}else m=_(p,i,d,y,b,n);h<=d&&(l+=i.slice(h,d)+m,h=d+p.length)}return l+i.slice(h)}]})},function(t,n,r){"use strict";var f=r(2),a=r(192),s=r(128);r(122)("search",1,function(e,i,u,c){return[function(t){var n=e(this),r=null==t?void 0:t[i];return void 0!==r?r.call(t,n):new RegExp(t)[i](String(n))},function(t){var n=c(u,t,this);if(n.done)return n.value;var r=f(t),e=String(this),i=r.lastIndex;a(i,0)||(r.lastIndex=0);var o=s(r,e);return a(r.lastIndex,i)||(r.lastIndex=i),null===o?-1:o.index}]})},function(t,n,r){"use strict";var l=r(124),x=r(2),m=r(119),S=r(136),w=r(8),_=r(128),h=r(152),e=r(4),O=Math.min,v=[].push,u="split",p="length",d="lastIndex",E=4294967295,M=!e(function(){RegExp(E,"y")});r(122)("split",2,function(i,o,y,g){var b;return b="c"=="abbc"[u](/(b)*/)[1]||4!="test"[u](/(?:)/,-1)[p]||2!="ab"[u](/(?:ab)*/)[p]||4!="."[u](/(.?)(.?)/)[p]||1<"."[u](/()()/)[p]||""[u](/.?/)[p]?function(t,n){var r=String(this);if(void 0===t&&0===n)return[];if(!l(t))return y.call(r,t,n);for(var e,i,o,u=[],c=(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":""),f=0,a=void 0===n?E:n>>>0,s=new RegExp(t.source,c+"g");(e=h.call(s,r))&&!(f<(i=s[d])&&(u.push(r.slice(f,e.index)),1<e[p]&&e.index<r[p]&&v.apply(u,e.slice(1)),o=e[0][p],f=i,u[p]>=a));)s[d]===e.index&&s[d]++;return f===r[p]?!o&&s.test("")||u.push(""):u.push(r.slice(f)),u[p]>a?u.slice(0,a):u}:"0"[u](void 0,0)[p]?function(t,n){return void 0===t&&0===n?[]:y.call(this,t,n)}:y,[function(t,n){var r=i(this),e=null==t?void 0:t[o];return void 0!==e?e.call(t,r,n):b.call(String(r),t,n)},function(t,n){var r=g(b,t,this,n,b!==y);if(r.done)return r.value;var e=x(t),i=String(this),o=m(e,RegExp),u=e.unicode,c=(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.unicode?"u":"")+(M?"y":"g"),f=new o(M?e:"^(?:"+e.source+")",c),a=void 0===n?E:n>>>0;if(0===a)return[];if(0===i.length)return null===_(f,i)?[i]:[];for(var s=0,l=0,h=[];l<i.length;){f.lastIndex=M?l:0;var v,p=_(f,M?i:i.slice(l));if(null===p||(v=O(w(f.lastIndex+(M?0:l)),i.length))===s)l=S(i,l,u);else{if(h.push(i.slice(s,l)),h.length===a)return h;for(var d=1;d<=p.length-1;d++)if(h.push(p[d]),h.length===a)return h;l=s=v}}return h.push(i.slice(s)),h}]})},function(t,n,r){"use strict";r(198);var e=r(2),i=r(115),o=r(10),u="toString",c=/./[u],f=function(t){r(27)(RegExp.prototype,u,t,!0)};r(4)(function(){return"/a/b"!=c.call({source:"a",flags:"b"})})?f(function(){var t=e(this);return"/".concat(t.source,"/","flags"in t?t.flags:!o&&t instanceof RegExp?i.call(t):void 0)}):c.name!=u&&f(function(){return c.call(this)})},function(t,n,r){"use strict";r(28)("anchor",function(n){return function(t){return n(this,"a","name",t)}})},function(t,n,r){"use strict";r(28)("big",function(t){return function(){return t(this,"big","","")}})},function(t,n,r){"use strict";r(28)("blink",function(t){return function(){return t(this,"blink","","")}})},function(t,n,r){"use strict";r(28)("bold",function(t){return function(){return t(this,"b","","")}})},function(t,n,r){"use strict";var e=r(1),i=r(131)(!1);e(e.P,"String",{codePointAt:function(t){return i(this,t)}})},function(t,n,r){"use strict";var e=r(1),u=r(8),c=r(155),f="endsWith",a=""[f];e(e.P+e.F*r(142)(f),"String",{endsWith:function(t){var n=c(this,t,f),r=1<arguments.length?arguments[1]:void 0,e=u(n.length),i=void 0===r?e:Math.min(u(r),e),o=String(t);return a?a.call(n,o,i):n.slice(i-o.length,i)===o}})},function(t,n,r){"use strict";r(28)("fixed",function(t){return function(){return t(this,"tt","","")}})},function(t,n,r){"use strict";r(28)("fontcolor",function(n){return function(t){return n(this,"font","color",t)}})},function(t,n,r){"use strict";r(28)("fontsize",function(n){return function(t){return n(this,"font","size",t)}})},function(t,n,r){var e=r(1),o=r(78),u=String.fromCharCode,i=String.fromCodePoint;e(e.S+e.F*(!!i&&1!=i.length),"String",{fromCodePoint:function(t){for(var n,r=[],e=arguments.length,i=0;i<e;){if(n=+arguments[i++],o(n,1114111)!==n)throw RangeError(n+" is not a valid code point");r.push(n<65536?u(n):u(55296+((n-=65536)>>10),n%1024+56320))}return r.join("")}})},function(t,n,r){"use strict";var e=r(1),i=r(155);e(e.P+e.F*r(142)("includes"),"String",{includes:function(t){return!!~i(this,t,"includes").indexOf(t,1<arguments.length?arguments[1]:void 0)}})},function(t,n,r){"use strict";r(28)("italics",function(t){return function(){return t(this,"i","","")}})},function(t,n,r){"use strict";var e=r(131)(!0);r(147)(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,n=this._t,r=this._i;return r>=n.length?{value:void 0,done:!0}:(t=e(n,r),this._i+=t.length,{value:t,done:!1})})},function(t,n,r){"use strict";r(28)("link",function(n){return function(t){return n(this,"a","href",t)}})},function(t,n,r){var e=r(1),u=r(33),c=r(8);e(e.S,"String",{raw:function(t){for(var n=u(t.raw),r=c(n.length),e=arguments.length,i=[],o=0;o<r;)i.push(String(n[o++])),o<e&&i.push(String(arguments[o]));return i.join("")}})},function(t,n,r){var e=r(1);e(e.P,"String",{repeat:r(156)})},function(t,n,r){"use strict";r(28)("small",function(t){return function(){return t(this,"small","","")}})},function(t,n,r){"use strict";var e=r(1),i=r(8),o=r(155),u="startsWith",c=""[u];e(e.P+e.F*r(142)(u),"String",{startsWith:function(t){var n=o(this,t,u),r=i(Math.min(1<arguments.length?arguments[1]:void 0,n.length)),e=String(t);return c?c.call(n,e,r):n.slice(r,r+e.length)===e}})},function(t,n,r){"use strict";r(28)("strike",function(t){return function(){return t(this,"strike","","")}})},function(t,n,r){"use strict";r(28)("sub",function(t){return function(){return t(this,"sub","","")}})},function(t,n,r){"use strict";r(28)("sup",function(t){return function(){return t(this,"sup","","")}})},function(t,n,r){"use strict";r(84)("trim",function(t){return function(){return t(this,3)}})},function(t,n,r){"use strict";var e=r(3),u=r(30),i=r(10),o=r(1),c=r(27),f=r(69).KEY,a=r(4),s=r(118),l=r(83),h=r(79),v=r(7),p=r(195),d=r(160),y=r(218),g=r(123),b=r(2),x=r(5),m=r(17),S=r(33),w=r(53),_=r(75),O=r(72),E=r(184),M=r(31),P=r(127),j=r(11),F=r(74),A=M.f,I=j.f,L=E.f,N=e.Symbol,T=e.JSON,k=T&&T.stringify,R="prototype",C=v("_hidden"),D=v("toPrimitive"),G={}.propertyIsEnumerable,W=s("symbol-registry"),U=s("symbols"),V=s("op-symbols"),B=Object[R],q="function"==typeof N&&!!P.f,z=e.QObject,K=!z||!z[R]||!z[R].findChild,H=i&&a(function(){return 7!=O(I({},"a",{get:function(){return I(this,"a",{value:7}).a}})).a})?function(t,n,r){var e=A(B,n);e&&delete B[n],I(t,n,r),e&&t!==B&&I(B,n,e)}:I,J=function(t){var n=U[t]=O(N[R]);return n._k=t,n},$=q&&"symbol"==typeof N.iterator?function(t){return"symbol"==typeof t}:function(t){return t instanceof N},Y=function(t,n,r){return t===B&&Y(V,n,r),b(t),n=w(n,!0),b(r),u(U,n)?(r.enumerable?(u(t,C)&&t[C][n]&&(t[C][n]=!1),r=O(r,{enumerable:_(0,!1)})):(u(t,C)||I(t,C,_(1,{})),t[C][n]=!0),H(t,n,r)):I(t,n,r)},X=function(t,n){b(t);for(var r,e=y(n=S(n)),i=0,o=e.length;i<o;)Y(t,r=e[i++],n[r]);return t},Q=function(t){var n=G.call(this,t=w(t,!0));return!(this===B&&u(U,t)&&!u(V,t))&&(!(n||!u(this,t)||!u(U,t)||u(this,C)&&this[C][t])||n)},Z=function(t,n){if(t=S(t),n=w(n,!0),t!==B||!u(U,n)||u(V,n)){var r=A(t,n);return!r||!u(U,n)||u(t,C)&&t[C][n]||(r.enumerable=!0),r}},tt=function(t){for(var n,r=L(S(t)),e=[],i=0;r.length>i;)u(U,n=r[i++])||n==C||n==f||e.push(n);return e},nt=function(t){for(var n,r=t===B,e=L(r?V:S(t)),i=[],o=0;e.length>o;)!u(U,n=e[o++])||r&&!u(B,n)||i.push(U[n]);return i};q||(c((N=function(){if(this instanceof N)throw TypeError("Symbol is not a constructor!");var n=h(0<arguments.length?arguments[0]:void 0),r=function(t){this===B&&r.call(V,t),u(this,C)&&u(this[C],n)&&(this[C][n]=!1),H(this,n,_(1,t))};return i&&K&&H(B,n,{configurable:!0,set:r}),J(n)})[R],"toString",function(){return this._k}),M.f=Z,j.f=Y,r(73).f=E.f=tt,r(117).f=Q,P.f=nt,i&&!r(68)&&c(B,"propertyIsEnumerable",Q,!0),p.f=function(t){return J(v(t))}),o(o.G+o.W+o.F*!q,{Symbol:N});for(var rt="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),et=0;rt.length>et;)v(rt[et++]);for(var it=F(v.store),ot=0;it.length>ot;)d(it[ot++]);o(o.S+o.F*!q,"Symbol",{for:function(t){return u(W,t+="")?W[t]:W[t]=N(t)},keyFor:function(t){if(!$(t))throw TypeError(t+" is not a symbol!");for(var n in W)if(W[n]===t)return n},useSetter:function(){K=!0},useSimple:function(){K=!1}}),o(o.S+o.F*!q,"Object",{create:function(t,n){return void 0===n?O(t):X(O(t),n)},defineProperty:Y,defineProperties:X,getOwnPropertyDescriptor:Z,getOwnPropertyNames:tt,getOwnPropertySymbols:nt});var ut=a(function(){P.f(1)});o(o.S+o.F*ut,"Object",{getOwnPropertySymbols:function(t){return P.f(m(t))}}),T&&o(o.S+o.F*(!q||a(function(){var t=N();return"[null]"!=k([t])||"{}"!=k({a:t})||"{}"!=k(Object(t))})),"JSON",{stringify:function(t){for(var n,r,e=[t],i=1;arguments.length>i;)e.push(arguments[i++]);if(r=n=e[1],(x(n)||void 0!==t)&&!$(t))return g(n)||(n=function(t,n){if("function"==typeof r&&(n=r.call(this,t,n)),!$(n))return n}),e[1]=n,k.apply(T,e)}}),N[R][D]||r(26)(N[R],D,N[R].valueOf),l(N,"Symbol"),l(Math,"Math",!0),l(e.JSON,"JSON",!0)},function(t,n,r){"use strict";var e=r(1),i=r(132),o=r(159),a=r(2),s=r(78),l=r(8),u=r(5),c=r(3).ArrayBuffer,h=r(119),v=o.ArrayBuffer,p=o.DataView,f=i.ABV&&c.isView,d=v.prototype.slice,y=i.VIEW,g="ArrayBuffer";e(e.G+e.W+e.F*(c!==v),{ArrayBuffer:v}),e(e.S+e.F*!i.CONSTR,g,{isView:function(t){return f&&f(t)||u(t)&&y in t}}),e(e.P+e.U+e.F*r(4)(function(){return!new v(2).slice(1,void 0).byteLength}),g,{slice:function(t,n){if(void 0!==d&&void 0===n)return d.call(a(this),t);for(var r=a(this).byteLength,e=s(t,r),i=s(void 0===n?r:n,r),o=new(h(this,v))(l(i-e)),u=new p(this),c=new p(o),f=0;e<i;)c.setUint8(f++,u.getUint8(e++));return o}}),r(77)(g)},function(t,n,r){var e=r(1);e(e.G+e.W+e.F*!r(132).ABV,{DataView:r(159).DataView})},function(t,n,r){r(57)("Float32",4,function(e){return function(t,n,r){return e(this,t,n,r)}})},function(t,n,r){r(57)("Float64",8,function(e){return function(t,n,r){return e(this,t,n,r)}})},function(t,n,r){r(57)("Int16",2,function(e){return function(t,n,r){return e(this,t,n,r)}})},function(t,n,r){r(57)("Int32",4,function(e){return function(t,n,r){return e(this,t,n,r)}})},function(t,n,r){r(57)("Int8",1,function(e){return function(t,n,r){return e(this,t,n,r)}})},function(t,n,r){r(57)("Uint16",2,function(e){return function(t,n,r){return e(this,t,n,r)}})},function(t,n,r){r(57)("Uint32",4,function(e){return function(t,n,r){return e(this,t,n,r)}})},function(t,n,r){r(57)("Uint8",1,function(e){return function(t,n,r){return e(this,t,n,r)}})},function(t,n,r){r(57)("Uint8",1,function(e){return function(t,n,r){return e(this,t,n,r)}},!0)},function(t,n,r){"use strict";var e=r(172),i=r(80);r(121)("WeakSet",function(t){return function(){return t(this,0<arguments.length?arguments[0]:void 0)}},{add:function(t){return e.def(i(this,"WeakSet"),t,!0)}},e,!1,!0)},function(t,n,r){"use strict";var e=r(1),i=r(173),o=r(17),u=r(8),c=r(21),f=r(138);e(e.P,"Array",{flatMap:function(t){var n,r,e=o(this);return c(t),n=u(e.length),r=f(e,0),i(r,e,e,n,0,1,t,arguments[1]),r}}),r(67)("flatMap")},function(t,n,r){"use strict";var e=r(1),i=r(173),o=r(17),u=r(8),c=r(49),f=r(138);e(e.P,"Array",{flatten:function(){var t=arguments[0],n=o(this),r=u(n.length),e=f(n,0);return i(e,n,n,r,0,void 0===t?1:c(t)),e}}),r(67)("flatten")},function(t,n,r){"use strict";var e=r(1),i=r(120)(!0);e(e.P,"Array",{includes:function(t){return i(this,t,1<arguments.length?arguments[1]:void 0)}}),r(67)("includes")},function(t,n,r){var e=r(1),i=r(150)(),o=r(3).process,u="process"==r(45)(o);e(e.G,{asap:function(t){var n=u&&o.domain;i(n?n.bind(t):t)}})},function(t,n,r){var e=r(1),i=r(45);e(e.S,"Error",{isError:function(t){return"Error"===i(t)}})},function(t,n,r){var e=r(1);e(e.G,{global:r(3)})},function(t,n,r){r(129)("Map")},function(t,n,r){r(130)("Map")},function(t,n,r){var e=r(1);e(e.P+e.R,"Map",{toJSON:r(171)("Map")})},function(t,n,r){var e=r(1);e(e.S,"Math",{clamp:function(t,n,r){return Math.min(r,Math.max(n,t))}})},function(t,n,r){var e=r(1);e(e.S,"Math",{DEG_PER_RAD:Math.PI/180})},function(t,n,r){var e=r(1),i=180/Math.PI;e(e.S,"Math",{degrees:function(t){return t*i}})},function(t,n,r){var e=r(1),o=r(181),u=r(179);e(e.S,"Math",{fscale:function(t,n,r,e,i){return u(o(t,n,r,e,i))}})},function(t,n,r){var e=r(1);e(e.S,"Math",{iaddh:function(t,n,r,e){var i=t>>>0,o=r>>>0;return(n>>>0)+(e>>>0)+((i&o|(i|o)&~(i+o>>>0))>>>31)|0}})},function(t,n,r){var e=r(1);e(e.S,"Math",{imulh:function(t,n){var r=+t,e=+n,i=65535&r,o=65535&e,u=r>>16,c=e>>16,f=(u*o>>>0)+(i*o>>>16);return u*c+(f>>16)+((i*c>>>0)+(65535&f)>>16)}})},function(t,n,r){var e=r(1);e(e.S,"Math",{isubh:function(t,n,r,e){var i=t>>>0,o=r>>>0;return(n>>>0)-(e>>>0)-((~i&o|~(i^o)&i-o>>>0)>>>31)|0}})},function(t,n,r){var e=r(1);e(e.S,"Math",{RAD_PER_DEG:180/Math.PI})},function(t,n,r){var e=r(1),i=Math.PI/180;e(e.S,"Math",{radians:function(t){return t*i}})},function(t,n,r){var e=r(1);e(e.S,"Math",{scale:r(181)})},function(t,n,r){var e=r(1);e(e.S,"Math",{signbit:function(t){return(t=+t)!=t?t:0==t?1/t==1/0:0<t}})},function(t,n,r){var e=r(1);e(e.S,"Math",{umulh:function(t,n){var r=+t,e=+n,i=65535&r,o=65535&e,u=r>>>16,c=e>>>16,f=(u*o>>>0)+(i*o>>>16);return u*c+(f>>>16)+((i*c>>>0)+(65535&f)>>>16)}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(21),u=r(11);r(10)&&e(e.P+r(126),"Object",{__defineGetter__:function(t,n){u.f(i(this),t,{get:o(n),enumerable:!0,configurable:!0})}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(21),u=r(11);r(10)&&e(e.P+r(126),"Object",{__defineSetter__:function(t,n){u.f(i(this),t,{set:o(n),enumerable:!0,configurable:!0})}})},function(t,n,r){var e=r(1),i=r(186)(!0);e(e.S,"Object",{entries:function(t){return i(t)}})},function(t,n,r){var e=r(1),f=r(187),a=r(33),s=r(31),l=r(139);e(e.S,"Object",{getOwnPropertyDescriptors:function(t){for(var n,r,e=a(t),i=s.f,o=f(e),u={},c=0;o.length>c;)void 0!==(r=i(e,n=o[c++]))&&l(u,n,r);return u}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(53),u=r(32),c=r(31).f;r(10)&&e(e.P+r(126),"Object",{__lookupGetter__:function(t){var n,r=i(this),e=o(t,!0);do{if(n=c(r,e))return n.get}while(r=u(r))}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(53),u=r(32),c=r(31).f;r(10)&&e(e.P+r(126),"Object",{__lookupSetter__:function(t){var n,r=i(this),e=o(t,!0);do{if(n=c(r,e))return n.set}while(r=u(r))}})},function(t,n,r){var e=r(1),i=r(186)(!1);e(e.S,"Object",{values:function(t){return i(t)}})},function(t,n,r){"use strict";var e=r(1),o=r(3),u=r(46),i=r(150)(),c=r(7)("observable"),f=r(21),a=r(2),s=r(70),l=r(76),h=r(26),v=r(71),p=v.RETURN,d=function(t){return null==t?void 0:f(t)},y=function(t){var n=t._c;n&&(t._c=void 0,n())},g=function(t){return void 0===t._o},b=function(t){g(t)||(t._o=void 0,y(t))},x=function(t,n){a(t),this._c=void 0,this._o=t,t=new m(this);try{var r=n(t),e=r;null!=r&&("function"==typeof r.unsubscribe?r=function(){e.unsubscribe()}:f(r),this._c=r)}catch(n){return void t.error(n)}g(this)&&y(this)};x.prototype=l({},{unsubscribe:function(){b(this)}});var m=function(t){this._s=t};m.prototype=l({},{next:function(t){var n=this._s;if(!g(n)){var r=n._o;try{var e=d(r.next);if(e)return e.call(r,t)}catch(t){try{b(n)}finally{throw t}}}},error:function(t){var n=this._s;if(g(n))throw t;var r=n._o;n._o=void 0;try{var e=d(r.error);if(!e)throw t;t=e.call(r,t)}catch(t){try{y(n)}finally{throw t}}return y(n),t},complete:function(t){var n=this._s;if(!g(n)){var r=n._o;n._o=void 0;try{var e=d(r.complete);t=e?e.call(r,t):void 0}catch(t){try{y(n)}finally{throw t}}return y(n),t}}});var S=function(t){s(this,S,"Observable","_f")._f=f(t)};l(S.prototype,{subscribe:function(t){return new x(t,this._f)},forEach:function(e){var i=this;return new(u.Promise||o.Promise)(function(t,n){f(e);var r=i.subscribe({next:function(t){try{return e(t)}catch(t){n(t),r.unsubscribe()}},error:n,complete:t})})}}),l(S,{from:function(t){var n="function"==typeof this?this:S,r=d(a(t)[c]);if(r){var e=a(r.call(t));return e.constructor===n?e:new n(function(t){return e.subscribe(t)})}return new n(function(n){var r=!1;return i(function(){if(!r){try{if(v(t,!1,function(t){if(n.next(t),r)return p})===p)return}catch(t){if(r)throw t;return void n.error(t)}n.complete()}}),function(){r=!0}})},of:function(){for(var t=0,n=arguments.length,e=new Array(n);t<n;)e[t]=arguments[t++];return new("function"==typeof this?this:S)(function(n){var r=!1;return i(function(){if(!r){for(var t=0;t<e.length;++t)if(n.next(e[t]),r)return;n.complete()}}),function(){r=!0}})}}),h(S.prototype,c,function(){return this}),e(e.G,{Observable:S}),r(77)("Observable")},function(t,n,r){"use strict";var e=r(1),i=r(46),o=r(3),u=r(119),c=r(191);e(e.P+e.R,"Promise",{finally:function(n){var r=u(this,i.Promise||o.Promise),t="function"==typeof n;return this.then(t?function(t){return c(r,n()).then(function(){return t})}:n,t?function(t){return c(r,n()).then(function(){throw t})}:n)}})},function(t,n,r){"use strict";var e=r(1),i=r(151),o=r(190);e(e.S,"Promise",{try:function(t){var n=i.f(this),r=o(t);return(r.e?n.reject:n.resolve)(r.v),n.promise}})},function(t,n,r){var e=r(56),i=r(2),o=e.key,u=e.set;e.exp({defineMetadata:function(t,n,r,e){u(t,n,i(r),o(e))}})},function(t,n,r){var e=r(56),o=r(2),u=e.key,c=e.map,f=e.store;e.exp({deleteMetadata:function(t,n){var r=arguments.length<3?void 0:u(arguments[2]),e=c(o(n),r,!1);if(void 0===e||!e.delete(t))return!1;if(e.size)return!0;var i=f.get(n);return i.delete(r),!!i.size||f.delete(n)}})},function(t,n,r){var o=r(199),u=r(167),e=r(56),i=r(2),c=r(32),f=e.keys,a=e.key,s=function(t,n){var r=f(t,n),e=c(t);if(null===e)return r;var i=s(e,n);return i.length?r.length?u(new o(r.concat(i))):i:r};e.exp({getMetadataKeys:function(t){return s(i(t),arguments.length<2?void 0:a(arguments[1]))}})},function(t,n,r){var e=r(56),i=r(2),o=r(32),u=e.has,c=e.get,f=e.key,a=function(t,n,r){if(u(t,n,r))return c(t,n,r);var e=o(n);return null!==e?a(t,e,r):void 0};e.exp({getMetadata:function(t,n){return a(t,i(n),arguments.length<3?void 0:f(arguments[2]))}})},function(t,n,r){var e=r(56),i=r(2),o=e.keys,u=e.key;e.exp({getOwnMetadataKeys:function(t){return o(i(t),arguments.length<2?void 0:u(arguments[1]))}})},function(t,n,r){var e=r(56),i=r(2),o=e.get,u=e.key;e.exp({getOwnMetadata:function(t,n){return o(t,i(n),arguments.length<3?void 0:u(arguments[2]))}})},function(t,n,r){var e=r(56),i=r(2),o=r(32),u=e.has,c=e.key,f=function(t,n,r){if(u(t,n,r))return!0;var e=o(n);return null!==e&&f(t,e,r)};e.exp({hasMetadata:function(t,n){return f(t,i(n),arguments.length<3?void 0:c(arguments[2]))}})},function(t,n,r){var e=r(56),i=r(2),o=e.has,u=e.key;e.exp({hasOwnMetadata:function(t,n){return o(t,i(n),arguments.length<3?void 0:u(arguments[2]))}})},function(t,n,r){var e=r(56),i=r(2),o=r(21),u=e.key,c=e.set;e.exp({metadata:function(r,e){return function(t,n){c(r,e,(void 0!==n?i:o)(t),u(n))}}})},function(t,n,r){r(129)("Set")},function(t,n,r){r(130)("Set")},function(t,n,r){var e=r(1);e(e.P+e.R,"Set",{toJSON:r(171)("Set")})},function(t,n,r){"use strict";var e=r(1),i=r(131)(!0);e(e.P,"String",{at:function(t){return i(this,t)}})},function(t,n,r){"use strict";var e=r(1),i=r(51),o=r(8),u=r(124),c=r(115),f=RegExp.prototype,a=function(t,n){this._r=t,this._s=n};r(146)(a,"RegExp String",function(){var t=this._r.exec(this._s);return{value:t,done:null===t}}),e(e.P,"String",{matchAll:function(t){if(i(this),!u(t))throw TypeError(t+" is not a regexp!");var n=String(this),r="flags"in f?String(t.flags):c.call(t),e=new RegExp(t.source,~r.indexOf("g")?r:"g"+r);return e.lastIndex=o(t.lastIndex),new a(e,n)}})},function(t,n,r){"use strict";var e=r(1),i=r(193),o=r(133),u=/Version\/10\.\d+(\.\d+)?( Mobile\/\w+)? Safari\//.test(o);e(e.P+e.F*u,"String",{padEnd:function(t){return i(this,t,1<arguments.length?arguments[1]:void 0,!1)}})},function(t,n,r){"use strict";var e=r(1),i=r(193),o=r(133),u=/Version\/10\.\d+(\.\d+)?( Mobile\/\w+)? Safari\//.test(o);e(e.P+e.F*u,"String",{padStart:function(t){return i(this,t,1<arguments.length?arguments[1]:void 0,!0)}})},function(t,n,r){"use strict";r(84)("trimLeft",function(t){return function(){return t(this,1)}},"trimStart")},function(t,n,r){"use strict";r(84)("trimRight",function(t){return function(){return t(this,2)}},"trimEnd")},function(t,n,r){r(160)("asyncIterator")},function(t,n,r){r(160)("observable")},function(t,n,r){var e=r(1);e(e.S,"System",{global:r(3)})},function(t,n,r){r(129)("WeakMap")},function(t,n,r){r(130)("WeakMap")},function(t,n,r){r(129)("WeakSet")},function(t,n,r){r(130)("WeakSet")},function(t,n,r){for(var e=r(162),i=r(74),o=r(27),u=r(3),c=r(26),f=r(82),a=r(7),s=a("iterator"),l=a("toStringTag"),h=f.Array,v={CSSRuleList:!0,CSSStyleDeclaration:!1,CSSValueList:!1,ClientRectList:!1,DOMRectList:!1,DOMStringList:!1,DOMTokenList:!0,DataTransferItemList:!1,FileList:!1,HTMLAllCollection:!1,HTMLCollection:!1,HTMLFormElement:!1,HTMLSelectElement:!1,MediaList:!0,MimeTypeArray:!1,NamedNodeMap:!1,NodeList:!0,PaintRequestList:!1,Plugin:!1,PluginArray:!1,SVGLengthList:!1,SVGNumberList:!1,SVGPathSegList:!1,SVGPointList:!1,SVGStringList:!1,SVGTransformList:!1,SourceBufferList:!1,StyleSheetList:!0,TextTrackCueList:!1,TextTrackList:!1,TouchList:!1},p=i(v),d=0;d<p.length;d++){var y,g=p[d],b=v[g],x=u[g],m=x&&x.prototype;if(m&&(m[s]||c(m,s,h),m[l]||c(m,l,g),f[g]=h,b))for(y in e)m[y]||o(m,y,e[y],!0)}},function(t,n,r){var e=r(1),i=r(158);e(e.G+e.B,{setImmediate:i.set,clearImmediate:i.clear})},function(t,n,r){var e=r(3),i=r(1),o=r(133),u=[].slice,c=/MSIE .\./.test(o),f=function(i){return function(t,n){var r=2<arguments.length,e=!!r&&u.call(arguments,2);return i(r?function(){("function"==typeof t?t:Function(t)).apply(this,e)}:t,n)}};i(i.G+i.B+i.F*c,{setTimeout:f(e.setTimeout),setInterval:f(e.setInterval)})},function(t,n,r){r(341),r(280),r(282),r(281),r(284),r(286),r(291),r(285),r(283),r(293),r(292),r(288),r(289),r(287),r(279),r(290),r(294),r(295),r(247),r(249),r(248),r(297),r(296),r(267),r(277),r(278),r(268),r(269),r(270),r(271),r(272),r(273),r(274),r(275),r(276),r(250),r(251),r(252),r(253),r(254),r(255),r(256),r(257),r(258),r(259),r(260),r(261),r(262),r(263),r(264),r(265),r(266),r(328),r(333),r(340),r(331),r(323),r(324),r(329),r(334),r(336),r(319),r(320),r(321),r(322),r(325),r(326),r(327),r(330),r(332),r(335),r(337),r(338),r(339),r(242),r(244),r(243),r(246),r(245),r(231),r(229),r(235),r(232),r(238),r(240),r(228),r(234),r(225),r(239),r(223),r(237),r(236),r(230),r(233),r(222),r(224),r(227),r(226),r(241),r(162),r(313),r(197),r(318),r(198),r(314),r(315),r(316),r(317),r(298),r(196),r(199),r(200),r(353),r(342),r(343),r(348),r(351),r(352),r(346),r(349),r(347),r(350),r(344),r(345),r(299),r(300),r(301),r(302),r(303),r(306),r(304),r(305),r(307),r(308),r(309),r(310),r(312),r(311),r(356),r(354),r(355),r(397),r(400),r(399),r(401),r(402),r(398),r(403),r(404),r(378),r(381),r(377),r(375),r(376),r(379),r(380),r(362),r(396),r(361),r(395),r(407),r(409),r(360),r(394),r(406),r(408),r(359),r(405),r(358),r(363),r(364),r(365),r(366),r(367),r(369),r(368),r(370),r(371),r(372),r(374),r(373),r(383),r(384),r(385),r(386),r(388),r(387),r(390),r(389),r(391),r(392),r(393),r(357),r(382),r(412),r(411),r(410),t.exports=r(46)},function(t,n){t.exports=function(t,n){if("string"==typeof n)return t.insertAdjacentHTML("afterend",n);var r=t.nextSibling;return r?t.parentNode.insertBefore(n,r):t.parentNode.appendChild(n)}}])</script><script src="/./main.a5fda8.js"></script><script>!function(){var e,t;e="/slider.27463f.js",t=document.createElement("script"),document.getElementsByTagName("body")[0].appendChild(t),t.setAttribute("src",e)}()</script>



<!--  -->

<script>
  /* 标签页标题切换 */
  var originTitle = document.title;
  var titleTime;
  document.addEventListener("visibilitychange", function () {
    if (document.hidden) {
      document.title = "(つェ⊂) 我藏好了哦~ " + originTitle;
      clearTimeout(titleTime);
    } else {
      document.title = "(*´∇｀*) 被你发现啦~ " + originTitle;
      titleTime = setTimeout(function () {
        document.title = originTitle;
      }, 2000);
    }
  });
</script>


    
<div class="tools-col" q-class="show:isShow,hide:isShow|isFalse" q-on="click:stop(e)">
  <div class="tools-nav header-menu">
    
    
      
      
      
    
      
      
      
    
      
      
      
    
    

    <ul style="width: 70%">
    
    
      
      <li style="width: 33.333333333333336%" q-on="click: openSlider(e, 'innerArchive')"><a href="javascript:void(0)" q-class="active:innerArchive">所有文章</a></li>
      
        
      
      <li style="width: 33.333333333333336%" q-on="click: openSlider(e, 'friends')"><a href="javascript:void(0)" q-class="active:friends">友链</a></li>
      
        
      
      <li style="width: 33.333333333333336%" q-on="click: openSlider(e, 'aboutme')"><a href="javascript:void(0)" q-class="active:aboutme">关于我</a></li>
      
        
    </ul>
  </div>
  <div class="tools-wrap">
    
    	<section class="tools-section tools-section-all" q-show="innerArchive">
        <div class="search-wrap">
          <input class="search-ipt" q-model="search" type="text" placeholder="find something…">
          <i class="icon-search icon" q-show="search|isEmptyStr"></i>
          <i class="icon-close icon" q-show="search|isNotEmptyStr" q-on="click:clearChose(e)"></i>
        </div>
        <div class="widget tagcloud search-tag">
          <p class="search-tag-wording">tag:</p>
          <label class="search-switch">
            <input type="checkbox" q-on="click:toggleTag(e)" q-attr="checked:showTags">
          </label>
          <ul class="article-tag-list" q-show="showTags">
            
            <div class="clearfix"></div>
          </ul>
        </div>
        <ul class="search-ul">
          <p q-show="jsonFail" style="padding: 20px; font-size: 12px;">
            缺失模块。<br/>1、请确保node版本大于6.2<br/>2、在博客根目录（注意不是yilia-plus根目录）执行以下命令：<br/> npm i hexo-generator-json-content --save<br/><br/>
            3、在根目录_config.yml里添加配置：
<pre style="font-size: 12px;" q-show="jsonFail">
  jsonContent:
    meta: false
    pages: false
    posts:
      title: true
      date: true
      path: true
      text: false
      raw: false
      content: false
      slug: false
      updated: false
      comments: false
      link: false
      permalink: false
      excerpt: false
      categories: false
      tags: true
</pre>
          </p>
          <li class="search-li" q-repeat="items" q-show="isShow">
            <a q-attr="href:path|urlformat" class="search-title"><i class="icon-quo-left icon"></i><span q-text="title"></span></a>
            <p class="search-time">
              <i class="icon-calendar icon"></i>
              <span q-text="date|dateformat"></span>
            </p>
            <p class="search-tag">
              <i class="icon-price-tags icon"></i>
              <span q-repeat="tags" q-on="click:choseTag(e, name)" q-text="name|tagformat"></span>
            </p>
          </li>
        </ul>
    	</section>
    

    
    	<section class="tools-section tools-section-friends" q-show="friends">
  		
        <ul class="search-ul">
          
            <li class="search-li">
              <a href="https://zhousiwei.gitee.io/ibooks/" target="_blank" 
                class="search-title" title="记录工作和学习过程中的笔记：Java、前端开发、Hexo博客、聚合支付、Linux笔记、ElasticSearch、ELK日志分析">
                
                  <img src="https://zhousiwei.gitee.io/ibooks/favicon.ico">
                
                技术笔记
              </a>
            </li>
          
            <li class="search-li">
              <a href="https://github.com/JoeyBling" target="_blank" 
                class="search-title" >
                
                  <i class="icon-link icon"></i>
                
                GitHub
              </a>
            </li>
          
            <li class="search-li">
              <a href="https://gitee.com/zhousiwei" target="_blank" 
                class="search-title" >
                
                  <i class="icon-link icon"></i>
                
                码云
              </a>
            </li>
          
            <li class="search-li">
              <a href="https://www.jianshu.com/u/02cbf31a043a" target="_blank" 
                class="search-title" >
                
                  <i class="icon-link icon"></i>
                
                简书
              </a>
            </li>
          
            <li class="search-li">
              <a href="https://blog.csdn.net/qq_30930805" target="_blank" 
                class="search-title" >
                
                  <i class="icon-link icon"></i>
                
                CSDN
              </a>
            </li>
          
        </ul>
  		
    	</section>
    

    
    	<section class="tools-section tools-section-me" q-show="aboutme">
        
          
  	  		<div class="aboutme-wrap" id="aboutme">主要涉及技术：<br>Java后端开发、聚合支付、<br>公众号开发、开源爱好者、Linux<br><br>联系QQ:2434387555<br><br>很惭愧<br><br>只做了一点微小的工作<br>谢谢大家</div>
  	  	
    	</section>
    
  </div>
  
</div>
    <!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                      <div class="pswp__preloader__cut">
                        <div class="pswp__preloader__donut"></div>
                      </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div> 
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>
  </div>

  
  
<script type="text/javascript" src="/plugins/activate-power-mode/activate-power-mode.js"></script>
<script>
  POWERMODE.colorful = true; // make power mode colorful
  POWERMODE.shake = false; // turn off shake
  document.body.addEventListener('input', POWERMODE);
</script>

  
  <!-- <script async type="text/javascript" size="90" alpha="0.2" zIndex="0" src="/plugins/ribbon.js/ribbon.min.js"></script> -->
  
  
  
</body>

</html>
